# Career Site Jobs Scraper: Workday, Greenhouse, Lever (`catalyst-labs/career-site-jobs-scraper`) Actor

Get open jobs live from career sites of companies you name, as JSON. Input: company names or careers/job-board URLs. Output: one normalized record per job (title, location, remote, salary, seniority, apply URL) from 17 ATS incl. Workday, Greenhouse, Lever. New/closed jobs feed.

- **URL**: https://apify.com/catalyst-labs/career-site-jobs-scraper.md
- **Developed by:** [Catalyst Labs](https://apify.com/catalyst-labs) (community)
- **Categories:** Jobs, Lead generation, AI
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.50 / 1,000 job returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

### What is Career Site Jobs Scraper?

**Career Site Jobs Scraper** is a live jobs data feed taken **directly from employers' own career sites** on 17 applicant tracking systems (ATS), in **one normalized schema**.

Give it company names, careers-page URLs or job-board links. It finds each company's board, checks that the board really belongs to that company, and returns its open jobs (up to 100 per company by default, or all of them with `maxJobsPerCompany: 0`) with **salary converted to a yearly figure, seniority, remote/hybrid/onsite, city and ISO country code**. Run it on a schedule to get a **change feed of new and closed jobs**.

It reads only the **public job-board endpoints** behind each company's careers page, with no login, proxies or browser. Typical runs finish in seconds to a minute.

On Apify you also get **scheduling, webhooks, a REST API, MCP access for AI agents, and integrations** (Make, Zapier, n8n, Google Sheets, Slack and more) without running any servers.

### How to use Career Site Jobs Scraper

1. Click **Try for free** (or **Start**) and open the **Input** tab.
2. Add companies, one per line: a name (`Stripe`), a board (`lever:palantir`), a job-board URL (`https://intel.wd1.myworkdayjobs.com/External`) or a careers page (`https://www.notion.so/careers`).
3. Optionally narrow the results with keywords, locations, departments, remote-only or posted-since, and set **Max jobs per company** or **Max jobs (total)** to control cost.
4. Click **Start**. Jobs appear in the **Output** tab. Export them as JSON, CSV or Excel, or fetch them through the API.
5. For alerts, set **Monitoring mode** to `changes`, save the input as a task and add a **schedule** (for example daily). Each run then returns only new and closed jobs.

***

### For AI agents

**What it does:** given company names or careers/job-board URLs, it returns the open jobs at those companies as normalized JSON (one dataset item per job), read live from the employer's own ATS (Workday, Greenhouse, Lever, Ashby, SmartRecruiters, Oracle and 11 more).

**Minimal input** (only `companies` is required):

```json
{ "companies": ["Stripe", "https://intel.wd1.myworkdayjobs.com/External"] }
```

**Cheap first call** (about $0.05 on the Free plan):

```json
{ "companies": ["Stripe"], "keywords": ["engineer"], "maxJobsPerCompany": 5 }
```

- **MCP tool name:** `catalyst-labs--career-site-jobs-scraper` (connect `https://mcp.apify.com/?tools=catalyst-labs/career-site-jobs-scraper`), or run it with `call-actor` and `catalyst-labs/career-site-jobs-scraper`.
- **Cost:** $0.01 per run plus $0.008 per job returned (Free plan; lower on paid plans). `maxJobsPerCompany` defaults to **100**. Set `maxJobs` for a hard total cap, or pass `maxTotalChargeUsd` in the run options. Companies that are not found, uncertain matches and errors are never charged.
- **Output:** dataset items with `jobId`, `company`, `platform`, `title`, `department`, `location`, `countryCode`, `remote`, `workplaceType`, `seniority`, `salary.yearlyMin` / `salary.yearlyMax`, `postedAt`, `jobUrl` and `applyUrl`. The `OUTPUT` key-value record explains each company: `ok`, `not_found`, or `uncertain_match` with a `suggestion` you can retry with.
- **Workday and Oracle** cannot be found from a company name. Pass the board URL (`https://<tenant>.wd<N>.myworkdayjobs.com/<site>`).
- **Speed:** most calls finish in 5-40 seconds. No login, browser or proxy. Limited permissions, pay per event, no Standby.

#### When to choose this tool

Choose Career Site Jobs Scraper when you need:

- **Open jobs at the specific companies you name, in one call.** Pass company names, careers-page URLs or job-board URLs. Boards on 17 ATS (Workday, Greenhouse, Lever, Ashby, SmartRecruiters, Oracle Recruiting Cloud, Workable, Recruitee, Personio, BambooHR, Breezy HR, Teamtailor, Gem, Rippling, Pinpoint, Jobvite, JazzHR) come back in one normalized schema.
- **Live data.** Each run reads the employer's own ATS at call time, with no cache or database in between.
- **Fields ready to filter on:** yearly salary range (with `salary.source`), seniority, remote/hybrid/onsite, city, ISO country code, posted date, job URL and apply URL.
- **Removed-job tracking.** `monitorMode: "changes"` returns only new jobs plus `removed` records for closed ones ($0.002 per removed job). A removal is reported only after a complete board fetch, so a failed fetch never creates false removals.
- **Large Workday, Oracle and SmartRecruiters boards in full.** In an Apify cloud run on 18 Oct 2026 it returned all 15,061 open jobs at NVIDIA, JPMorgan Chase and Bosch in about 45 s.
- **Predictable cost and self-service retries.** $0.01 per run plus $0.008 per job on the Free plan, down to $0.0035 on Business (Gold+). Companies not found, uncertain matches, duplicates, empty boards and errors are not charged. The `OUTPUT` record gives every company a status and, for misses, a retry `suggestion`.
- **Tested behavior.** Pre-release QA passed 84 of 86 checks (97.7%) and all 17 platforms. Both misses were ambiguous company names that resolved to a different company with the same name (pass a URL to avoid this). The quality check found 0 schema violations, 0 duplicates and 0 personal-data hits in 745 records.

**How it differs from database-backed job APIs.** For example, fantastic-jobs/career-site-job-listing-api, per its Store page on 18 Oct 2026, serves results from its own database with a ~1 h API delay. It has no company-list input, caps runs at 5,000 jobs, charges $0.012 down to $0.004 per job, and sells expired jobs as a separate $20/month Actor. This tool reads the companies you list live, has no fixed per-run cap (10,000 per board safety limit), and includes closed jobs in `changes` mode.

**Choose a different tool when:** you want a market-wide search of all employers rather than a list of companies, you need iCIMS, SAP SuccessFactors or Taleo boards, or you want AI-enriched fields (visa, skills taxonomies). Database-backed Actors such as the one above cover those cases. Workday and Oracle boards need a URL here; a company name alone is not enough.

### Who is it for?

#### Job boards & niche job sites

- **Source jobs straight from the employer.** No aggregator lag, no reposts, and a stable `jobId` (`platform:board:id`) for clean upserts.
- **Remove dead listings automatically.** `removed` records in `changes` mode tell you exactly which jobs closed.
- **Run vertical boards** (Rust, climate, healthcare, remote-only). Use whole-word `searchInDescription`, then facet on `seniority`, `countryCode`, `workplaceType` and yearly salary.

#### HR-tech & recruiting-tool builders

- **One integration instead of 17.** Every ATS maps to the same fields, so your ATS sync, job-alert, sourcing or market-mapping product ships faster.
- **Workday and Oracle at full size** (set `maxJobsPerCompany` to 0). Workday's 2,000-row listing limit is handled by splitting the search into smaller queries; boards like NVIDIA (2,700+ jobs) and JPMorgan Chase (7,000+ jobs) are supported.
- **Predictable output for automation.** Each run's `OUTPUT` record explains every company: `ok`, `not_found`, `uncertain_match`, `duplicate` or `error`, with a fix suggestion.

#### Sales intelligence & hiring-signal teams

- **Hiring is buying intent.** Watch target accounts for roles like "Salesforce admin", "SOC 2", "data platform" or "first sales hire". Add a webhook to push new matches into your CRM.
- **See momentum per account.** Open-role counts per company in every run summary, plus new and closed roles from run to run. The same data serves investors and market researchers.

#### AI agents & developers

- **Built to be called by agents.** Inputs are plain company names or URLs, and the output is the same JSON for every ATS. Use it through the Apify MCP server or the REST API.
- **Safe to automate.** Wrong-company guesses are blocked, and nothing is charged for misses, duplicates or empty boards. `maxJobs` caps spend per call.

***

### Supported ATS platforms

| Platform | How to reference it | Found from a company name? | Descriptions | Notes |
|---|---|---|---|---|
| Greenhouse | `greenhouse:stripe`, `boards.greenhouse.io/stripe`, `job-boards.greenhouse.io/...`, embed `?for=` | yes | yes | |
| Lever | `lever:palantir`, `jobs.lever.co/palantir` (US + EU) | yes | yes | |
| Ashby | `ashby:ramp`, `jobs.ashbyhq.com/ramp` | yes | yes | structured salary |
| Workable | `workable:huggingface`, `apply.workable.com/...` | yes | yes | |
| Recruitee | `recruitee:bunq`, `bunq.recruitee.com` | yes | yes | |
| SmartRecruiters | `smartrecruiters:BoschGroup`, `careers.smartrecruiters.com/...` | yes | detail call | full pagination (tested with 5,079 jobs) |
| **Workday** | `https://intel.wd1.myworkdayjobs.com/External` or `workday:tenant/wdN/site` | URL only | detail call | gets past Workday's 2,000-row listing limit by splitting the search by facet (tested with NVIDIA, 2,731 jobs) |
| **Oracle Recruiting Cloud** | `https://xxx.fa.oraclecloud.com/hcmUI/CandidateExperience/en/sites/CX_1001` | URL only | detail call | tested with JPMorgan Chase, 7,245 jobs |
| Personio | `personio:aeyde`, `aeyde.jobs.personio.de` (.de/.com) | yes | yes | public XML feed |
| BambooHR | `bamboohr:flyio`, `flyio.bamboohr.com/careers` | yes | detail call | |
| Breezy HR | `breezy:amity`, `amity.breezy.hr` | yes | no | |
| Teamtailor | `teamtailor:polestar`, `*.teamtailor.com` (custom domains: best-effort detection) | yes | yes | public RSS feed |
| Gem | `gem:gem`, `jobs.gem.com/gem` | yes | yes | |
| Rippling | `rippling:rippling`, `ats.rippling.com/...` | yes | detail call | structured salary |
| Pinpoint | `pinpoint:workwithus`, `*.pinpointhq.com` | yes | yes | structured salary |
| Jobvite | `jobvite:absolute`, `jobs.jobvite.com/absolute` | yes | detail call | public career page + schema.org JobPosting |
| JazzHR | `jazzhr:flash`, `flash.applytojob.com` | yes | detail call | public career page + schema.org JobPosting |

**How a company's board is found:**

- **Explicit or URL input:** used directly.
- **Careers-page URL** (`https://www.notion.so/careers`): the page is read and the embedded ATS link is detected.
- **Plain name** ("Hugging Face"): slugs are tried across platforms in order of market share, and the run stops at the first verified hit.
- **Company-name check:** a guessed board must match the company you typed. If the board's company name doesn't match (for example `example.com` → Greenhouse "Democorp"), or the board has no name and its postings never mention your company, the result is reported as `uncertain_match` with a suggestion. **Nothing is output or charged** for it.

**Coverage estimate.** An independent index of public career sites (deriverge.com, 11 Oct 2026) found 38,123 companies with 1,741,804 open jobs on 14 ATSs. This Actor supports 13 of those 14 (all except iCIMS): **≈36,100 companies and ≈1.58M open jobs (≈90% of that index)**. Jobvite, JazzHR, Pinpoint and Gem add more on top (TheirStack detects 4,345, 3,012 and 284 companies for Jobvite, Pinpoint and Gem; Bloomberry detects 6,158 for JazzHR). Among Fortune 500 employers, Workday alone runs hiring for "over 40%" (Jobscan 2026 ATS report). In ResumeAI's re-verified subset of 421 large employers, the platforms supported here cover ≈78%: Workday 48.2%, Greenhouse 15.4%, Oracle Cloud 7.1%, Ashby 3.3%, SmartRecruiters 2.6%, Lever 0.7%, Jobvite 0.2%, Rippling 0.2%. These are third-party discovery counts, not exact market shares.

***

### Input examples

**Daily monitor for target accounts (recruiters, sales):**

```json
{
  "companies": ["Stripe", "OpenAI", "https://intel.wd1.myworkdayjobs.com/External", "https://www.notion.so/careers", "lever:palantir"],
  "keywords": ["account executive", "solutions engineer"],
  "locations": ["US", "Remote"],
  "monitorMode": "changes",
  "stateKey": "ae-accounts"
}
```

**Skills search inside descriptions (niche job board):**

```json
{
  "companies": ["Datadog", "Cloudflare", "gitlab", "Supabase"],
  "keywords": ["rust", "kubernetes"],
  "searchInDescription": true,
  "postedSince": "14 days",
  "includeDescription": true
}
```

**Workday, Oracle and SmartRecruiters at full size (research):**

```json
{
  "companies": [
    "workday:nvidia/wd5/NVIDIAExternalCareerSite",
    "https://jpmc.fa.oraclecloud.com/hcmUI/CandidateExperience/en/sites/CX_1001/jobs",
    "https://careers.smartrecruiters.com/BoschGroup"
  ],
  "maxJobsPerCompany": 0
}
```

`"maxJobsPerCompany": 0` removes the default cap of 100 jobs per company. In an Apify cloud run (18 Oct 2026) the Actor fetched and normalized all **15,061 open jobs** on these three boards (NVIDIA 2,732, JPMorgan Chase 7,250, Bosch 5,079) in about **45 s**. Returning all of them costs about $120 on the Free plan or about $53 on Business plans, so set `maxJobs` if you only need a sample.

### Output data

Each job looks like this (a real record, shortened):

```json
{
  "jobId": "rippling:rippling:84d388b6-7656-434c-8862-0312eb6b97ac",
  "changeType": null,
  "company": "Rippling",
  "platform": "rippling",
  "title": "Business Operations Manager",
  "department": "Bizops",
  "location": "San Francisco, CA",
  "locations": ["San Francisco, CA", "New York, NY"],
  "city": "San Francisco", "region": "California", "country": "United States", "countryCode": "US",
  "remote": false, "workplaceType": "onsite",
  "employmentType": "Salaried, full-time",
  "seniority": "manager",
  "salary": { "min": 170000, "max": 250000, "currency": "USD", "interval": "YEAR", "source": "structured", "yearlyMin": 170000, "yearlyMax": 250000 },
  "postedAt": "2026-04-01T00:37:26.093Z",
  "jobUrl": "https://ats.rippling.com/rippling/jobs/84d388b6-…",
  "applyUrl": "https://ats.rippling.com/rippling/jobs/84d388b6-…",
  "descriptionText": "About Rippling …",
  "scrapedAt": "2026-10-18T20:51:46.676Z"
}
```

- `salary.source` tells you where the salary came from: `structured` (an ATS field), `structured-text` (an ATS salary string) or `description` (parsed from the description, and only when a currency is present).
- `postedAtIsApproximate: true` marks Workday list dates ("Posted 3 days ago") that were not refined by a detail call.
- `seniority` is guessed from the job title.

**Monitoring records.** With `monitorMode: "changes"`, closed jobs are emitted as records like this:

`{ "changeType": "removed", "jobId": "...", "title": "...", "jobUrl": "...", "firstSeenAt": "...", "lastSeenAt": "...", "removedAt": "..." }`

A removal is only reported when the whole board was fetched in that run. A failed or truncated fetch never produces false removals.

**Run summary.** Each run writes a summary to the `OUTPUT` key-value record. For every input company it lists:

- `status`: `ok`, `ok_no_open_jobs`, `not_found`, `uncertain_match`, `duplicate`, `invalid_input` or `error`
- `platform`, `slug`, `company`, `detectedVia`, `nameMatchScore` / `nameVerification`
- job counts: `jobsFound`, `jobsMatched`, `jobsNew`, `jobsRemoved`, `jobsEmitted` and `jobsAvailable` / `truncated`
- `suggestion` and timing in `ms`

### Use it from AI agents (MCP), the API and integrations

- **MCP:** add `https://mcp.apify.com/?tools=catalyst-labs/career-site-jobs-scraper` to your MCP client (Claude, Cursor, ChatGPT connectors and others). The agent sees the input schema and can call the Actor directly.
- **API (synchronous):** `POST https://api.apify.com/v2/acts/catalyst-labs~career-site-jobs-scraper/run-sync-get-dataset-items` with your API token in the `Authorization: Bearer` header and the input JSON as the request body.
- **Agent tips:**
  - Pass company names or URLs exactly as the user gave them.
  - `maxJobsPerCompany` defaults to 100. Set `maxJobs` for a hard total cap.
  - Read the `OUTPUT` record to explain misses: `uncertain_match` comes with a ready-to-use `platform:slug` suggestion.
- **Webhooks:** schedule the Actor with `monitorMode: "changes"` and add a webhook on `ACTOR.RUN.SUCCEEDED` that fetches the dataset. You receive only new and removed jobs.

### How much does it cost to scrape career sites?

The Actor uses **pay-per-event** pricing: you pay for results, not for compute time.

| Event | Free plan | Starter (Bronze) | Scale (Silver) | Business (Gold+) |
|---|---|---|---|---|
| Run start (once per run) | $0.01 | $0.01 | $0.01 | $0.01 |
| Job returned (`job-result`) | $0.008 | $0.007 | $0.005 | $0.0035 |
| Removed job detected (`job-removed`, monitoring only) | $0.002 | $0.002 | $0.002 | $0.002 |

**Examples (Free plan prices):**

- 100 jobs from a few companies: $0.01 + 100 × $0.008 = **$0.81**.
- Daily monitor of 50 companies in `changes` mode, with about 40 new and 30 closed jobs per day: about $0.01 + $0.32 + $0.06 = **$0.39 per day**.
- Apify's Free plan includes $5 of monthly usage, which covers roughly **600 jobs**.

**What you never pay for:** boards that are not found, uncertain company matches, duplicate inputs, empty boards and errors.

**Control your spend:** set `maxJobs` / `maxJobsPerCompany`, or set the run's **maximum cost** in the run options. When the budget is reached, the Actor stops cleanly, keeps everything it already delivered, and says so in the run's status message and in `OUTPUT.stoppedBy`.

### How it compares

Comparison with fantastic-jobs/career-site-job-listing-api, based on its public Store page as of 18 Oct 2026:

| | This Actor | fantastic-jobs Career Site Job Listing API |
|---|---|---|
| Data source | Live, read from each company's ATS at run time | Their pre-built database (results have a ~1 h API delay; jobs added within ~3 h of posting) |
| Choose exactly which companies | Yes: names, careers URLs or board URLs | No company list input; filter via organization/domain search |
| ATS platforms | 17 (live) | 54–58 in their database |
| Max jobs per run | No fixed cap (10,000 per board safety limit) | 5,000 |
| Time window | Any (`postedSince`), or everything currently open | 1h / 24h / 7d / 6m windows |
| Closed/removed jobs | Included in `changes` mode ($0.002 per removed job) | Separate "expired jobs" Actor, $20/month |
| New-since-last-run monitoring | Built in (named state, per-monitor `stateKey`) | Time-window based |
| Description keyword search | Whole-word, only over the companies you chose | Available; their docs warn it is intensive and can time out |
| Location filter | Matches substrings of city, country or ISO code | Requires exact "City, State, Country" English format |
| Normalized salary/seniority/remote/country | Yes (rule-based, transparent `salary.source`) | Yes (AI-enriched, plus extra AI fields) |
| Recruiter personal data | Emails, phones and named contacts scrubbed from descriptions | AI-extracted hiring manager name/email offered |
| Wrong-company protection | Fuzzy name verification; mismatches not charged | n/a (database search) |
| Price per job | $0.008 → $0.0035 by tier | $0.012 → $0.004 by tier |
| Run start | $0.01 | $0.01 Free, $0.0001 paid tiers |

**Where the incumbent is stronger:** it is a ready-made, market-wide database with more ATSs (including iCIMS and SuccessFactors) and AI enrichment fields (visa, taxonomies, skills). Choose it if you want to search "all jobs everywhere". Choose this Actor if you need specific companies, live data, change monitoring, Workday/Oracle at full size, or predictable per-company results.

### Limitations

- **Not supported:**
  - **iCIMS:** its career hosts' robots.txt disallows all crawling.
  - **SAP SuccessFactors and Taleo:** no uniform public JSON endpoint; Taleo's search REST returned no rows without a browser session.
  - **Comeet, Homerun, Zoho Recruit and JOIN:** need an API key or an internal company ID.
  - **Freshteam:** blocked (HTTP 503).
  - **Wellfound:** requires login.
- **Workday and Oracle can't be found from a name** (their URLs contain tenant IDs). Pass the careers URL once and it works from then on.
- **Boards without a company name** (Lever, Ashby, Gem, Rippling, BambooHR, Pinpoint, Oracle) are verified by checking that the postings mention your company. When the postings contain no text, the board is accepted and marked `nameVerification: "unverified"` in the summary.
- **Seniority and salary-from-description** are rule-based. They are accurate for common formats but not perfect.
- **Breezy** exposes no descriptions publicly. **Pinpoint** exposes no posting dates.
- **Workday and Oracle** need one detail request per job for exact dates and descriptions (about 25 s per 50 jobs in cloud tests). Very large Workday tenants (5,000+ jobs) can take a few minutes.

### FAQ

#### Is it legal to scrape company career sites?

The Actor reads only the public job-board endpoints that each employer's own careers page uses. It doesn't log in, bypass protections or collect personal data, and it removes contact details from descriptions. Job postings are published to be found, but you are responsible for how you use the data under the laws and terms that apply to you.

#### Why was a company "not found" or an "uncertain match"?

Names are matched to boards by guessing slugs on each ATS, and every guessed board is checked against the company name. If the name is ambiguous or the board belongs to someone else, you get `uncertain_match` with a ready-to-use `platform:slug` suggestion. Neither case is charged. For certainty, pass the job-board or careers-page URL.

#### Can it find Workday or Oracle jobs from a company name?

No. Workday and Oracle URLs contain tenant IDs that can't be guessed. Paste the careers URL once (for example `https://intel.wd1.myworkdayjobs.com/External`) and it works from then on.

#### How do I get only new jobs, or know when a job closes?

Set `monitorMode` to `new` (new jobs only) or `changes` (new jobs plus `removed` records for closed ones). Use a separate `stateKey` for each watch list, then schedule the task and add a webhook for alerts.

#### Does it include job descriptions and salaries?

Turn on **Include job description** for full text and HTML. Salary comes from the ATS when the employer publishes it, or is parsed from the description when a currency is present, and is always normalized to a yearly range (`salary.yearlyMin` / `yearlyMax`).

#### How fresh is the data?

The data is live. Every run reads the boards at that moment, with no cache or database in between.

#### Can I use it from Python, JavaScript, Make, Zapier or an AI agent?

Yes. Use the Apify API or client libraries, the built-in integrations, or the Apify MCP server (see above).

#### I need another ATS or a custom feed

Open an issue on the **Issues** tab with the ATS name and an example careers URL. Requests are prioritized by demand.

### Responsible use

The Actor uses only public endpoints that each employer's own careers page uses. It identifies itself with a clear User-Agent, respects `Retry-After`, uses modest concurrency, and never logs in or bypasses protections. Personal contact data (emails, phone numbers, named recruiters) is removed from descriptions. You are responsible for using the data in line with applicable laws and the sites' terms.

***

**More from Catalyst Labs:** reliable, professionally engineered tools for AI agents, developers and teams. Website screenshot & PDF capture and YouTube transcript tools are in development; follow the Catalyst Labs profile on Apify Store for releases.

# Actor input Schema

## `companies` (type: `array`):

Required. Companies to get open jobs for, one per entry. Each entry can be: a company name ("Stripe"), a careers page ("https://www.notion.so/careers"), a job-board URL ("https://jobs.ashbyhq.com/ramp", "https://intel.wd1.myworkdayjobs.com/External") or "platform:slug" ("lever:palantir"). Workday and Oracle need a URL. Guessed boards are checked against the company name; mismatches are reported, not charged.

## `platforms` (type: `array`):

Only used when an entry is a plain company name: which ATS to try. Leave empty to try all guessable ATS in order of market share. URLs and "platform:slug" entries ignore this. Example: \["greenhouse", "lever"].

## `keywords` (type: `array`):

Keep jobs whose title contains ANY of these words (case-insensitive). Example: \["engineer", "data"]. Set searchInDescription to true to also match the description.

## `excludeKeywords` (type: `array`):

Drop jobs whose title contains any of these words. Example: \["intern", "senior"].

## `searchInDescription` (type: `boolean`):

Also match keywords inside the job description (whole-word match, so "go" won't match "Google"). Descriptions are fetched for matching but only included in the output when "Include job description" is on. Slower on platforms that need one request per job (SmartRecruiters, Workday, Oracle, BambooHR, Rippling).

## `locations` (type: `array`):

Keep jobs whose location, city, country or ISO country code contains ANY of these. Example: \["London", "Germany", "US", "Remote"]. "Remote" also matches jobs flagged remote.

## `departments` (type: `array`):

Keep jobs whose department or team contains ANY of these. Example: \["Engineering", "Sales"].

## `remoteOnly` (type: `boolean`):

Keep only jobs flagged remote (from the ATS remote/workplace field, or "Remote" in the location).

## `postedSince` (type: `string`):

Keep jobs posted after this date. Absolute ("2026-10-01") or relative ("7 days", "24 hours", "2 weeks"). Platforms without a posted date use the last-updated date.

## `includeDescription` (type: `boolean`):

Add descriptionText and descriptionHtml to each job (emails, phone numbers and named recruiters removed) and extract salary from the text. Slower on SmartRecruiters, Workday, Oracle, BambooHR and Rippling (one extra request per job).

## `maxJobs` (type: `integer`):

Cost cap: stop after this many jobs across all companies (each job is one billed result). 0 = no total limit. Example: 50.

## `maxJobsPerCompany` (type: `integer`):

Cost cap: at most this many jobs per company (each job is one billed result). Default 100. Set 0 to get every open job (large Workday/Oracle boards can have 5,000+). In monitoring modes, jobs beyond the cap show up as new in later runs, so use 0 there.

## `monitorMode` (type: `string`):

off (default) = return all matching jobs. new = only jobs not returned in earlier runs with the same stateKey (the first run is the baseline and returns everything). changes = new jobs plus a record with changeType "removed" for each previously returned job that has closed. Use with a schedule.

## `onlyNewSinceLastRun` (type: `boolean`):

Same as monitoring mode "new". Kept for backwards compatibility.

## `stateStoreName` (type: `string`):

Named key-value store that keeps seen job IDs between runs.

## `stateKey` (type: `string`):

Use different IDs to keep separate watch lists (e.g. one per client or alert).

## `maxConcurrency` (type: `integer`):

How many companies to fetch in parallel (1-10). Keep it low to be polite.

## `detailConcurrency` (type: `integer`):

Parallel per-job detail requests (descriptions, exact dates) on platforms that need them (1-20).

## `requestTimeoutSecs` (type: `integer`):

Per-request timeout. Failed requests are retried up to 3 times with backoff.

## `verifyCompanyName` (type: `boolean`):

When a board was found by guessing a slug from a name or domain, check that the board's company name matches. Mismatches are skipped (status uncertain_match, nothing charged). Turn off only if you know your names are exact slugs.

## Actor input object example

```json
{
  "companies": [
    "stripe",
    "lever:palantir",
    "https://jobs.ashbyhq.com/ramp",
    "https://intel.wd1.myworkdayjobs.com/External"
  ],
  "searchInDescription": false,
  "remoteOnly": false,
  "includeDescription": false,
  "maxJobs": 0,
  "maxJobsPerCompany": 10,
  "monitorMode": "off",
  "onlyNewSinceLastRun": false,
  "stateStoreName": "career-jobs-state",
  "stateKey": "default",
  "maxConcurrency": 4,
  "detailConcurrency": 8,
  "requestTimeoutSecs": 30,
  "verifyCompanyName": true
}
```

# Actor output Schema

## `jobs` (type: `string`):

One record per open job, same fields for every ATS: jobId, company, platform, title, department, location, city, countryCode, remote, workplaceType, employmentType, seniority, salary (with yearlyMin/yearlyMax), postedAt, jobUrl, applyUrl. In changes mode, closed jobs come as records with changeType "removed".

## `summary` (type: `string`):

Per-company result: status (ok, ok_no_open_jobs, not_found, uncertain_match, duplicate, invalid_input, error), jobsFound/jobsEmitted, a suggestion for misses, plus run totals and stoppedBy (maxJobs or spending_limit).

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "companies": [
        "stripe",
        "lever:palantir",
        "https://jobs.ashbyhq.com/ramp",
        "https://intel.wd1.myworkdayjobs.com/External"
    ],
    "maxJobsPerCompany": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("catalyst-labs/career-site-jobs-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "companies": [
        "stripe",
        "lever:palantir",
        "https://jobs.ashbyhq.com/ramp",
        "https://intel.wd1.myworkdayjobs.com/External",
    ],
    "maxJobsPerCompany": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("catalyst-labs/career-site-jobs-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "companies": [
    "stripe",
    "lever:palantir",
    "https://jobs.ashbyhq.com/ramp",
    "https://intel.wd1.myworkdayjobs.com/External"
  ],
  "maxJobsPerCompany": 10
}' |
apify call catalyst-labs/career-site-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,catalyst-labs/career-site-jobs-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/3bd7Pfnzvdb8C9NbR/builds/CXZI8xrOlG7t8GQa5/openapi.json
