# ATS Jobs Scraper: Lever, Greenhouse, Ashby, Workday + 8 More (`yugenox/ats-jobs-scraper`) Actor

Jobs from any company on 12 ATSs: Lever (US+EU), Greenhouse, Ashby, Workday, Phenom, Workable, Recruitee, BambooHR, Jobvite, Breezy HR, Personio, Rippling. One format: salary, remote/hybrid, country, department, description, apply link. Country filter, company directory, monitor mode. No login.

- **URL**: https://apify.com/yugenox/ats-jobs-scraper.md
- **Developed by:** [Yugenox Corp](https://apify.com/yugenox) (community)
- **Categories:** Jobs, Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.50 / 1,000 job postings

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## ATS Jobs Scraper: Lever, Greenhouse, Ashby, Workday, BambooHR, Jobvite & more

Get every open job from any company that hires through one of **12 applicant tracking systems**: **Lever**, **Greenhouse**, **Ashby**, **Workday**, **Phenom**, **Workable**, **Recruitee**, **BambooHR**, **Jobvite**, **Breezy HR**, **Personio** and **Rippling**. Type company names or paste job-board URLs. You get one clean row per job in the same format for every ATS: title, department, location, country, remote/hybrid/on-site, employment type, **salary range**, posting date, full description and the apply link.

- **No login, no cookies, no API keys.** Add companies and run.
- **One format for 12 ATSs.** Mix a Lever startup, a Workday enterprise, a Phenom careers site (Air Canada, Bell, BAE Systems, Allianz) and a BambooHR small business in the same run, and every row has the same fields.
- **Type just a company name.** "FedEx", "TD", "Walmart", "Stripe": the actor finds the company's **real** job board(s) for you, including big employers on Workday (every public career site of the company is read: 12 for FedEx). A built-in directory of about 1,000 large employers answers instantly; anything else is found from the company's careers page, its name on every ATS, and a web search as a last resort. Or paste any link: `jobs.lever.co/palantir`, `acme.wd5.myworkdayjobs.com/External`, `acme.bamboohr.com/careers`, a single job link, or a careers page. **Lever's separate EU region** is covered too.
- **Honest answers when there is nothing to scrape.** If a company posts its jobs on a platform this actor doesn't read (Oracle, SuccessFactors, iCIMS, Taleo, Avature ...) or on its own site (Amazon, Google, Apple), the run says so by name instead of returning an empty board. You're never charged for that.
- **Full boards, even the huge ones.** Workday career sites normally stop showing results after 2,000 jobs. This actor splits big sites into smaller searches, so you get all of them (NVIDIA: 2,655 of 2,655). Jobvite boards come back complete in one go, with full descriptions.
- **Structured salary.** Min, max, currency and pay period wherever the company publishes pay: Lever, Greenhouse, Ashby, Recruitee and Rippling pay ranges, plus pay written as text on BambooHR and Breezy ("$22 - $28 per hour").
- **Search companies you don't know yet.** Turn on the built-in **company directory** to search thousands of small and mid-size employers on BambooHR, Recruitee, Jobvite, Workable, Breezy HR, Personio and Rippling at once, filtered by job title, country and more.
- **Monitor mode.** Schedule it daily and get only **new** and **removed** jobs since the last run.
- **Only the countries you want.** Set `countries` (`CA`, `US`, `Canada`, ...) and you get only jobs located there, from every board of every company; leave it empty for all countries. Big employers' career sites for other regions are skipped entirely, so a FedEx search for Canada reads 2 of its 12 Workday sites. See "Country filter" below.
- **Filters that forgive typos.** Keywords, locations, countries, remote/hybrid, departments, employment type, posting date and "salary only". All filters are case-insensitive, and jobs you filter out are not charged.

### Supported ATSs and what you can put in "Companies"

| ATS | Paste a link like | Or a prefix |
|---|---|---|
| Lever (US and EU) | `https://jobs.lever.co/palantir`, `https://jobs.eu.lever.co/seb` | `lever:spotify`, `lever-eu:seb` |
| Greenhouse | `https://job-boards.greenhouse.io/anthropic`, `boards.greenhouse.io/stripe` | `greenhouse:stripe` |
| Ashby | `https://jobs.ashbyhq.com/openai` | `ashby:openai` |
| Workday | `https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite`, `https://wd3.myworkdaysite.com/recruiting/td/TD_Bank_Careers` | (link only) |
| Phenom | The company's own careers site, e.g. `https://careers.aircanada.com/ca/en/search-results`, `https://jobs.bell.ca` | `phenom:careers.aircanada.com` |
| Workable | `https://apply.workable.com/huggingface/` | `workable:huggingface` |
| Recruitee | `https://trafilea.recruitee.com`, or the company's own Recruitee careers domain | `recruitee:trafilea` |
| BambooHR | `https://weber.bamboohr.com/careers` | `bamboohr:weber` |
| Jobvite | `https://jobs.jobvite.com/nutanix/jobs` | `jobvite:nutanix` |
| Breezy HR | `https://navaide.breezy.hr` | `breezy:navaide` |
| Personio | `https://codecentric.jobs.personio.de` (or `.jobs.personio.com`) | `personio:codecentric` |
| Rippling | `https://ats.rippling.com/rippling/jobs` | `rippling:rippling` |

You can also use:

| Input | Example | What happens |
|---|---|---|
| Company name | `FedEx`, `TD`, `Palantir`, `Air Canada`, `Hugging Face` | The company's real board(s) on any of the 12 ATSs, Workday and Phenom included (see "How company names are found") |
| Single job link | `https://jobs.lever.co/zoox/f4746da4-...`, `https://weber.bamboohr.com/careers/33` | Just that job |
| Careers page | `https://www.example.com/careers` | The embedded job board is detected (any of the 12 ATSs; a careers site that runs on Phenom is read directly) |
| Filtered Workday site | `https://rbi.wd3.myworkdayjobs.com/RBI_External_Career_Site?q=Tim%20Hortons` | Only the jobs that site's own search (`q=`) or filters (facet ids in the URL) return, the same as on the page |

#### How company names are found

1. **Big-company directory.** About 1,400 large employers (Fortune 1000, TSX, well-known tech). Every board in it was checked against what the board itself says it is (its own company name, website and job text, the Workday site's own title), so `FedEx`, `TD Bank`, `Toronto-Dominion` and `J&J` resolve instantly and cost nothing extra, and "Metro" never lands on *Metro Public Adjustment*.
2. **The company's careers page** (careers.<company>.com, /careers, /jobs, one level of links): any job-board link on it is followed, and a careers site that runs on Phenom (Air Canada, Bell, OpenText) is read directly. A Workday site found there is expanded to all of the company's public career sites. If the site is shared by a whole group (a parent company's site listing all its brands), it is narrowed to the company you asked for; when nothing narrows it, the rows are labelled with the site's own name and the run says they include other businesses. A careers page that redirects to its parent's careers site (ecobee.com/careers goes to Generac's) gets the parent's jobs narrowed to the brand, and the run says so.
3. **The name on every ATS** (plus `<name>io`, `get<name>`, `<name>jobs` ...), Workday tenants included. A board found this way is only used when **the board itself names the company**: "Wise Worksite Field Sales" is not Wise, a Workday tenant called "wf" is Wells Fargo's, a BambooHR trial account with the vendor's demo jobs is nobody's. On the smaller ATSs the name alone is not enough: the board's own website must be the company's ("GONG" on Recruitee links gong.pl, a Polish agency, so "Gong" finds Gong.io's Greenhouse board instead), and for a well-known employer its jobs must be where the company hires (a London recruiter's "boltjobs" is not Bolt). A board with 0 open jobs never wins over one with jobs.
4. **A web search**, only if all of that found nothing (at most 2 per company, remembered for later runs). A search result is only a lead: the board it points to must name the company too, so a board still named after the company's former name counts when the search shows it (Greenhouse "tripactions" calls itself Navan).

A board that only its name ties to the company (no website of its own, or one that is not the company's) is used last, after the search, and the run says so: "Acme was matched only by its name on Recruitee 'acme' — if those jobs are not Acme's, paste its careers page or job-board URL instead".

Rows are only ever labelled with a company's name when its board is known to be that company's. If nothing trustworthy is found, the run says so and what to paste (the company's careers page or its board URL), and you pay nothing. Platforms the actor doesn't read are named ("FreshBooks posts its jobs on Gem (jobs.gem.com/freshbooks)"). If you pasted a careers page that links no job board, the answer asks for the board URL itself. For a lesser-known name, an empty board may be offered with a clear note: "Found Workable board 'acme' but it has 0 open jobs — if that's not the right company, paste its careers page URL". What each company resolved to (and how) is saved in the run's `RUN_REPORT` record, and remembered for 30 days so the next run goes straight there.

#### Country filter

Put the countries you want in `countries`: 2-letter codes (`CA`, `US`, `GB`, `DE`) or English names (`Canada`, `United States`), in any case. Leave it empty and you get every country (the default).

- A job counts when **any** of its locations is in one of those countries: `Toronto, ON`, `Austin, TX, United States`, a multi-location job with one office in Canada. States and provinces are understood (`London, ON` is in Canada, `London` alone is the UK one, `San Jose, CA` is California), and so is a country code after a region or a city (`Toronto, ON, CA` is Canada, `Hamburg, DE` is Germany, `Wilmington, DE` is Delaware).
- **Remote jobs** count when their region covers the country: `Remote - US` is a US job, `Remote - North America` counts for Canada and the US, `Remote - EMEA` for Germany. A remote job with no place at all (`Remote`, `Anywhere`, `Worldwide`) is left out unless you turn on `includeRemoteAnywhere`.
- Jobs outside your countries are dropped **before** they are saved, so you are never charged for them, and `maxJobsPerCompany` counts only jobs in your countries.
- A big employer's career sites for other regions are skipped from the site's own country counts (a FedEx search for Canada reads only its Canadian Workday sites), and a big global site is narrowed to your countries' jobs. The log says, per company: `🌍 fedex: 53 matched in CA out of 1,196 (10 of 12 career sites skipped: no jobs there)`.
- Every row carries `jobCountry`: the country the job was counted in (with the filter, the one you asked for; empty for a region-wide or anywhere-remote match).

```json
{
  "companies": ["FedEx", "Air Canada", "Walmart", "https://jobs.lever.co/palantir"],
  "countries": ["CA"],
  "maxJobsPerCompany": 50
}
```

#### Company directory: search without company names

Turn on `searchDirectory` and the actor also reads the job boards of companies in its built-in directory: thousands of employers on BambooHR, Recruitee, Jobvite, Workable, Breezy HR, Personio and Rippling that currently have open jobs, biggest boards first. Use it with your filters to find, for example, every remote "data analyst" job at small companies in Canada:

```json
{
  "searchDirectory": true,
  "keywords": ["data analyst"],
  "countries": ["CA"],
  "workplaceTypes": ["remote", "hybrid"],
  "maxDirectoryCompanies": 500
}
```

`countries` also narrows the directory to companies that hire there, `directoryAts` picks which ATSs to include, and `directoryCompanyKeywords` keeps companies whose name contains a word ("health", "bank"). You can combine the directory with your own `companies` list. The directory is refreshed regularly.

If a company can't be found, the run keeps going. That company gets one free row with `rowType: "error"` that explains what happened.

### Input

| Field | Type | Description |
|---|---|---|
| `companies` | array | Company names, job-board URLs, job URLs or careers pages (see above). Required unless you search the company directory |
| `ats` | string | For company names only: `auto` (default) or one ATS (`lever`, `greenhouse`, `ashby`, `workday`, `phenom`, `workable`, `recruitee`, `bamboohr`, `breezy`, `jobvite`, `personio`, `rippling`) |
| `maxJobsPerCompany` | integer | Cap per company. Empty = every open job |
| `maxJobs` | integer | Cap for the whole run |
| `keywords` | array | Keep jobs whose title contains any of these |
| `searchInDescription` | boolean | Also match keywords in the department, team and description |
| `excludeKeywords` | array | Drop jobs whose title contains any of these |
| `locations` | array | Keep jobs whose location contains any of these (city, state, country or "remote") |
| `countries` | array | Keep only jobs in these countries (`CA`, `US`, `GB`, `Canada`, `Germany`, ...). Empty = every country (see "Country filter") |
| `includeRemoteAnywhere` | boolean | With `countries`: also keep remote jobs that name no country (`Remote`, `Anywhere`). Default off |
| `workplaceTypes` | array | `onsite`, `hybrid`, `remote` |
| `departments` | array | Keep jobs whose department or team contains any of these |
| `employmentTypes` | array | e.g. `full-time`, `part-time`, `contract`, `intern` |
| `postedWithinDays` | integer | Only jobs published in the last N days |
| `salaryOnly` | boolean | Only jobs with a published salary range |
| `includeDescription` | string | `plain` (default), `html`, `both` or `none` |
| `includeCompanyInfo` | boolean | Add the company's display name and logo (and website on Ashby) where the ATS doesn't include them already |
| `searchDirectory` | boolean | Also search the built-in company directory (see above) |
| `directoryAts` | array | Directory ATSs to include: `bamboohr`, `recruitee`, `jobvite`, `workable`, `breezy`, `personio`, `rippling` (empty = all) |
| `directoryCompanyKeywords` | array | Only directory companies whose name contains any of these |
| `maxDirectoryCompanies` | integer | How many directory companies to search (default 200, biggest first) |
| `monitorMode` | boolean | Save only new and removed jobs since the previous run |
| `monitorKey` | string | Optional name for a monitored list, to keep separate histories |
| `includeUnchanged` | boolean | In monitor mode, also save jobs that didn't change |
| `maxConcurrency` | integer | Job boards read in parallel (default 5) |

```json
{
  "companies": [
    "palantir",
    "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite",
    "https://jobs.jobvite.com/nutanix",
    "bamboohr:weber",
    "Hugging Face"
  ],
  "keywords": ["engineer", "data"],
  "countries": ["US", "GB"],
  "workplaceTypes": ["remote", "hybrid"],
  "postedWithinDays": 30
}
```

### Output

One row per job:

```json
{
  "rowType": "job",
  "ats": "recruitee",
  "company": "Viderity Inc.",
  "companySlug": "viderity",
  "companyLogo": null,
  "companyWebsite": null,
  "region": null,
  "jobId": "2733899",
  "title": "Backend Developer - Senior",
  "department": "NSF",
  "team": null,
  "location": "DC, Washington, United States",
  "allLocations": ["Washington D.C. Metro Area (D.C., Maryland, Virginia)"],
  "country": "US",
  "jobCountry": "US",
  "workplaceType": "onsite",
  "employmentType": "Full-time",
  "salaryMin": 87000,
  "salaryMax": 95000,
  "salaryCurrency": "USD",
  "salaryInterval": "year",
  "salaryText": "$87,000 - $95,000 USD per year",
  "createdAt": "2026-09-04T03:00:46.000Z",
  "updatedAt": "2026-09-15T13:40:53.000Z",
  "jobUrl": "https://viderity.recruitee.com/o/backend-developer-senior",
  "applyUrl": "https://viderity.recruitee.com/o/backend-developer-senior/c/new",
  "requisitionId": null,
  "descriptionPlain": "Java Developer with extensive experience developing cloud-native, microservices-based applications...",
  "input": "https://viderity.recruitee.com",
  "scrapedAt": "2026-09-24T08:31:12.402Z"
}
```

| Field | Notes |
|---|---|
| `ats` | `lever`, `greenhouse`, `ashby`, `workday`, `phenom`, `workable`, `recruitee`, `bamboohr`, `jobvite`, `breezy`, `personio` or `rippling` |
| `region` | Lever: `us` or `eu`. Greenhouse: `eu` for EU-hosted boards. Otherwise empty |
| `country` | 2-letter country code (from the ATS, or worked out from the location) |
| `jobCountry` | 2-letter code of the country the job was counted in: with `countries`, the one it matched (a multi-location job in New York and Toronto is `CA` in a Canada search); empty for a region-wide or anywhere-remote match. Without `countries`, the job's own country |
| `workplaceType` | `onsite`, `hybrid`, `remote`, or `null` when the board doesn't say |
| `salaryMin` / `salaryMax` / `salaryCurrency` / `salaryInterval` | Filled when the company publishes pay. `salaryInterval` is `year`, `month`, `week`, `day` or `hour`. `salaryText` keeps the wording the company used |
| `createdAt` | When the job was published (ISO 8601) |
| `updatedAt` | Last update (Greenhouse, Ashby and Recruitee) |
| `descriptionPlain` / `descriptionHtml` | Depending on `includeDescription`. On Lever, the plain text includes the bullet lists and closing section |
| `lists` | Lever only: the posting's bullet sections (responsibilities, requirements, ...) as structured lists |
| `requisitionId` | The company's requisition number when published (Greenhouse, Jobvite, Workday, Workable) |
| `allLocations` | Every location of a multi-location job |
| `changeType`, `firstSeenAt`, `removedDetectedAt` | Monitor mode only (see below) |
| `input` | The line from `companies` that produced this row |

Error rows look like `{ "rowType": "error", "input": "notacompany", "error": "no Lever, Greenhouse, Ashby, ... job board found ..." }`. They are free.

### Monitor mode: only new and removed jobs

Turn on `monitorMode` and schedule the task (daily, for example):

- **First run**: every current job is saved with `changeType: "new"`. This is the baseline.
- **Later runs**: only jobs that appeared since the last run (`"new"`) and jobs that were closed (`"removed"`, with `firstSeenAt` and `removedDetectedAt`).
- A job is only reported as removed after its board was read completely **and** the job itself no longer exists. A temporary outage never looks like a wave of closed jobs.
- History is kept per set of companies and filters. Changing the list or a filter starts a new baseline. Use `monitorKey` to name a list explicitly.

Most daily runs save just a handful of rows, which keeps scheduled tracking cheap.

### Use cases

- **Job boards and aggregators**: pull fresh jobs from startups (Lever, Ashby), scale-ups (Greenhouse, Workable, Rippling), enterprises (Workday), European employers (Personio, Recruitee) and small and mid-size businesses (BambooHR, Breezy, Jobvite), all in one format.
- **Recruiting and sales intelligence**: see who is hiring for what, where, and at what pay. Hiring bursts in a department are a strong buying signal.
- **Salary benchmarking**: collect published salary ranges by title, location and company.
- **Job seekers**: watch your dream companies and get only new postings every morning.
- **Market research and investing**: track headcount growth and hiring trends over time with monitor mode.
- **AI and RAG pipelines**: clean plain-text descriptions, ready to embed.

### FAQ

**Which companies can I scrape?**
Any company whose careers site runs on one of the 12 supported ATSs. That covers most tech companies and startups (Palantir, Spotify, Stripe, Anthropic, OpenAI, Hugging Face), large enterprises on Workday (NVIDIA, banks, retailers) or on a Phenom careers site (Air Canada, Bell Canada, OpenText, BAE Systems, Allianz, Bechtel, United Airlines) and many thousands of small and mid-size businesses on BambooHR, Jobvite, Breezy, Recruitee and Personio. If a company name isn't found, paste its job-board or careers-page URL instead.

**How do I find a company's job-board URL?**
Open any job on the company's careers page. If the address or the "Apply" button goes to `lever.co`, `greenhouse.io`, `ashbyhq.com`, `myworkdayjobs.com`, `workable.com`, `recruitee.com`, `bamboohr.com`, `jobvite.com`, `breezy.hr`, `personio.de` or `rippling.com`, paste that link. If the careers site is a Phenom site (its job pages look like `careers.<company>.com/<country>/<language>/job/...`), paste the careers page itself or `phenom:careers.<company>.com`.

**Why do some jobs have no salary?**
Salary is only included when the company publishes it on the posting. Coverage varies by company and country. Use `salaryOnly` to keep only jobs that have one.

**Why is `workplaceType` sometimes null?**
Some boards don't say whether a role is remote, hybrid or on-site. When the location text says "Remote", the job is marked remote.

**Why are dates or salaries empty for some BambooHR, Rippling or Workday jobs?**
On these ATSs the job list only has titles and locations; descriptions, exact dates and pay are read job by job. That happens automatically unless you set `includeDescription` to `none` and use no filters, which trades those fields for speed.

**My filters returned nothing. Why?**
The run log lists the actual locations, departments and employment types each board uses, so you can adjust your filters.

**How fast is it?**
Most boards finish in a few seconds. A Workday site with 2,600 jobs takes about 2 minutes, and boards that need one request per job (BambooHR, Rippling, Workday with descriptions) take longer in proportion to their size. Several companies are read in parallel.

**Do I need a proxy?**
Apify Proxy is on by default and failing requests are retried on fresh IPs automatically. You don't need to change anything.

**What happens when something goes wrong?**
The run still finishes successfully with everything it could collect. A company that can't be found or a board that stays unreachable gets one free `rowType: "error"` row that explains why; a job-board outage pauses the run for a moment and retries before giving up; and if the run is about to hit its time limit it stops early, saves what it has and reports "Partial" in the status message. You only pay for the job rows that were actually delivered.

**Can I use `maxItems` or `limit` instead of `maxJobs`?**
Yes. `maxItems`, `maxResults` and `limit` all mean `maxJobs`; `startUrls` works for `companies`; `keyword`, `location` and `country` work as single-value versions of the filters.

**Is it legal to scrape job postings from Lever, Greenhouse, Workday and the other ATSs?**
This actor collects only public job postings: the same listings each company publishes on its careers site for anyone to read, without logging in. Job descriptions are written by the employer and sometimes include a recruiter's name or contact email, which can count as personal data. If you store or reuse it, make sure you have a legitimate reason to, and follow data-protection laws such as GDPR (EU and UK), PIPEDA (Canada) and CCPA (California), as well as the terms of the websites you collect from. If you're unsure whether your use case is allowed, check with a lawyer. For background, read Apify's guide [Is web scraping legal?](https://blog.apify.com/is-web-scraping-legal/)

**Does it access any private data?**
No. It reads only what each company's public job board shows to every visitor: no login, no cookies, no API keys, and no candidate or applicant data. Postings a company has marked as unlisted are skipped, internal fields that some job feeds carry (such as hiring-team names, referral bonuses or recruiting workflow) are never read, and application mailbox addresses are never saved.

# Actor input Schema

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

One per line. Paste a job-board URL, a single job URL, a company careers page, or just a company name ("FedEx", "TD", "Palantir"). Supported: Lever (jobs.lever.co, jobs.eu.lever.co), Greenhouse (job-boards.greenhouse.io/acme), Ashby (jobs.ashbyhq.com/acme), Workday (acme.wd5.myworkdayjobs.com/External), Workable (apply.workable.com/acme), Recruitee (acme.recruitee.com), BambooHR (acme.bamboohr.com/careers), Jobvite (jobs.jobvite.com/acme), Breezy HR (acme.breezy.hr), Personio (acme.jobs.personio.de) and Rippling (ats.rippling.com/acme/jobs). A company name finds the company's real board(s) on any of these ATSs, Workday included: a built-in directory of ~1,000 large employers first, then its careers page, its name on each ATS and, as a last resort, a web search. If it posts on a platform this actor doesn't read, the run says which one. You can also force the ATS with a prefix: lever:spotify, lever-eu:seb, greenhouse:stripe, ashby:openai, workable:huggingface, recruitee:trafilea, bamboohr:weber, jobvite:nutanix, breezy:navaide, personio:codecentric, rippling:rippling.

## `ats` (type: `string`):

Only used for plain company names and careers pages. Auto finds the company's real board on any supported ATS (directory, careers page, the name on every ATS incl. Workday, then a web search). Pick one ATS to look only there.

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

Cap for each company. Leave empty to get every open job on the board.

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

Cap for the whole run. Leave empty for no limit.

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

Keep jobs whose title contains any of these words or phrases (case-insensitive). Example: engineer, data, product manager.

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

Match keywords against the department, team and full job description too, not just the title.

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

Drop jobs whose title contains any of these. Example: intern, senior, sales.

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

Keep jobs whose location contains any of these (city, state, country or "remote"). Example: London, New York, Canada, remote.

## `countries` (type: `array`):

Keep only jobs in these countries. Leave empty for every country (the default). 2-letter codes (CA, US, GB, DE) or English names (Canada, United States, Germany), any case. A job with several locations counts when any of them is in one of these countries; a remote job counts when its region covers the country ("Remote - US", "Remote - North America"). Jobs outside these countries are never saved or charged.

## `includeRemoteAnywhere` (type: `boolean`):

With "Countries" set: also keep remote jobs that name no country or region at all ("Remote", "Anywhere", "Worldwide"). Off by default, so a Canada search returns only jobs that are in Canada or remote within a region that covers it.

## `workplaceTypes` (type: `array`):

Keep only on-site, hybrid or remote jobs. Jobs whose board does not say are dropped when this is set.

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

Keep jobs whose department or team contains any of these. Example: Engineering, Sales, Design.

## `employmentTypes` (type: `array`):

Keep jobs whose employment type contains any of these. Example: full-time, part-time, contract, intern.

## `postedWithinDays` (type: `integer`):

Only jobs first published in the last N days.

## `salaryOnly` (type: `boolean`):

Keep only jobs that publish a structured salary range.

## `includeDescription` (type: `string`):

Plain text is best for search and AI. HTML keeps the formatting. None gives a much smaller dataset and is faster on BambooHR, Rippling and Workday, whose descriptions, posting dates and pay are read job by job (with None and no filters those fields stay empty there).

## `includeCompanyInfo` (type: `boolean`):

Read the company's display name and logo from its job board (website too on Ashby). Adds one small request per company. Most other ATSes include the name for free.

## `searchDirectory` (type: `boolean`):

Also search thousands of companies you did not list: every company in the built-in directory of BambooHR, Recruitee, Jobvite, Workable, Breezy HR, Personio and Rippling job boards that currently has open jobs (biggest boards first). Combine it with Job title keywords, Countries and the other filters to find matching jobs across all of them. "Companies" can be left empty.

## `directoryAts` (type: `array`):

Leave empty for all of them.

## `directoryCompanyKeywords` (type: `array`):

Only directory companies whose name contains any of these (e.g. health, bank, logistics). Countries set under Filters also narrow the directory to companies hiring there.

## `maxDirectoryCompanies` (type: `integer`):

How many directory companies to search, biggest boards first.

## `monitorMode` (type: `boolean`):

Remember which jobs each board had last time and save only what changed: new jobs (changeType "new") and closed ones (changeType "removed"). The first run saves every job as new. Schedule it daily to track hiring. State is kept per set of companies and filters.

## `monitorKey` (type: `string`):

Optional. Give each monitored list its own name to keep separate histories. Leave empty and one is derived from your companies and filters.

## `includeUnchanged` (type: `boolean`):

In monitor mode, also save jobs that were already there last time (changeType "unchanged").

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

How many job boards to read at the same time.

## `proxyConfiguration` (type: `object`):

Apify Proxy is used by default. Failing requests are retried on fresh IPs automatically.

## Actor input object example

```json
{
  "companies": [
    "palantir",
    "https://jobs.eu.lever.co/seb",
    "https://job-boards.greenhouse.io/anthropic",
    "https://jobs.ashbyhq.com/zapier",
    "https://weber.bamboohr.com/careers",
    "https://trafilea.recruitee.com",
    "https://jobs.jobvite.com/uplight"
  ],
  "ats": "auto",
  "maxJobsPerCompany": 25,
  "searchInDescription": false,
  "includeRemoteAnywhere": false,
  "salaryOnly": false,
  "includeDescription": "plain",
  "includeCompanyInfo": false,
  "searchDirectory": false,
  "maxDirectoryCompanies": 200,
  "monitorMode": false,
  "includeUnchanged": false,
  "maxConcurrency": 5,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (type: `string`):

All scraped job postings.

## `run` (type: `string`):

Status and statistics for this run.

# 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": [
        "palantir",
        "https://jobs.eu.lever.co/seb",
        "https://job-boards.greenhouse.io/anthropic",
        "https://jobs.ashbyhq.com/zapier",
        "https://weber.bamboohr.com/careers",
        "https://trafilea.recruitee.com",
        "https://jobs.jobvite.com/uplight"
    ],
    "maxJobsPerCompany": 25,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("yugenox/ats-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": [
        "palantir",
        "https://jobs.eu.lever.co/seb",
        "https://job-boards.greenhouse.io/anthropic",
        "https://jobs.ashbyhq.com/zapier",
        "https://weber.bamboohr.com/careers",
        "https://trafilea.recruitee.com",
        "https://jobs.jobvite.com/uplight",
    ],
    "maxJobsPerCompany": 25,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("yugenox/ats-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": [
    "palantir",
    "https://jobs.eu.lever.co/seb",
    "https://job-boards.greenhouse.io/anthropic",
    "https://jobs.ashbyhq.com/zapier",
    "https://weber.bamboohr.com/careers",
    "https://trafilea.recruitee.com",
    "https://jobs.jobvite.com/uplight"
  ],
  "maxJobsPerCompany": 25,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call yugenox/ats-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,yugenox/ats-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/tf0AKqcwzB8arNnWQ/builds/LmKIXRFTgcZefJYEH/openapi.json
