# Greenhouse, Lever, Workday & iCIMS Jobs Scraper — 24 ATS (`memo23/career-site-ats-jobs-api`) Actor

Live jobs from any company across 24 ATS platforms — Greenhouse, Lever, Ashby, Workday, SmartRecruiters, Personio, iCIMS, Taleo, Recruitee, Workable, BambooHR, Teamtailor and more — fetched at run time, not from a stale database. One unified schema, plus the hiring company's contact email.

- **URL**: https://apify.com/memo23/career-site-ats-jobs-api.md
- **Developed by:** [Muhamed Didovic](https://apify.com/memo23) (community)
- **Categories:** Jobs, AI, Agents
- **Stats:** 31 total users, 20 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $2.50 / 1,000 jobs

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Multi-ATS Jobs + Company Emails Scraper

The fastest way to turn a list of companies into live, structured job data — plus a contact email for outreach.
Point it at any company across 24 major ATS platforms — from startup favorites (Greenhouse, Lever, Ashby, Workable, Recruitee, Breezy, Personio, BambooHR, SmartRecruiters, Teamtailor, Pinpoint, Jobvite, Comeet/Spark Hire) to the enterprise suites where the Fortune 500 hire (Workday, iCIMS classic & modern, Oracle Cloud HCM, Taleo, SAP SuccessFactors, UKG Pro/UltiPro, ADP Workforce Now, Eightfold AI, Phenom, BrassRing/IBM Kenexa) — by board URL, bare company name, or an uploaded sheet of links. Every job is fetched **live from the company's own ATS at run time**, normalized to one schema, and optionally paired with the hiring company's public contact email.

Unlike pre-indexed job databases, nothing here is served from a cache: no stale postings, no missing companies, no "job not in our database yet." If the role is on the company's board right now, you get it.

![How the Multi-ATS Jobs + Emails Scraper works](https://raw.githubusercontent.com/muhamed-didovic/muhamed-didovic.github.io/main/assets/how-it-works-ats-jobs.png)

***

### Why Use This Scraper?

- ✅ **Live results, not a stale database** — every row is fetched from the company's ATS at the moment you run, so closed roles disappear and new roles appear instantly
- ✅ **24 ATS platforms, one schema** — Greenhouse, Lever, Ashby, SmartRecruiters, Workday, Workable, Recruitee, Breezy, Personio, BambooHR, iCIMS (classic & modern), Oracle Cloud HCM, Taleo, SAP SuccessFactors, UKG Pro (UltiPro), ADP Workforce Now, Eightfold AI, Phenom, Jobvite, Teamtailor, Pinpoint, BrassRing, Comeet — all normalized so you write one parser
- ✅ **Enterprise coverage** — the Fortune 500 hire on Workday, iCIMS, Oracle (HCM + Taleo), SAP, UKG, ADP, Phenom and BrassRing; paste the career-site URL and those boards are reachable too
- ✅ **Any company, not a fixed index** — job database APIs only cover companies they've already crawled; here you name the company and it's covered
- ✅ **ATS auto-detection** — paste a bare company name and the actor probes the platforms to find its board; or paste any board URL and it routes by host
- ✅ **Company contact email** — flip on `enrichEmails` to turn a job feed into a lead list with a best-effort public contact address per company
- ✅ **Filters applied before billing** — title, keywords, location, department, remote, posted-after; you only pay for rows that match
- ✅ **Bulk input via spreadsheet** — feed an Excel/CSV/Google Sheet of career links or company names for hands-off list processing

***

### Overview

The Multi-ATS Jobs + Company Emails Scraper is built for recruiting teams, sales teams using hiring as a buying signal, and job-board or talent-intelligence builders who need structured job data straight from company career sites.

The output is job-shaped rows: one dataset row per open position. Whether you start from a board URL, a bare company name, or an uploaded sheet, the resulting dataset is a single stream of normalized job rows — tagged with `inputIndex` so rows stay traceable to the input that produced them.

Two things distinguish it from job-database APIs on the Store. First, coverage is **whatever you ask for**: databases can only return companies they already index, and their public issue trackers fill up with "this job is missing" reports — here there is no index to be missing from. Second, freshness is **run-time**: `publishedAt`, locations, and descriptions come from the ATS response at the moment of your run, not from the last crawl cycle.

***

### Supported Inputs

#### ATS board URLs

| ATS | Input example | Notes |
|---|---|---|
| **Greenhouse** | `https://boards.greenhouse.io/stripe` or `stripe` | Full content inline |
| **Lever** | `https://jobs.lever.co/spotify` or `spotify` | Full content inline |
| **Ashby** | `https://jobs.ashbyhq.com/ramp` or `ramp` | Full content inline |
| **SmartRecruiters** | `https://careers.smartrecruiters.com/BoschGroup` or `BoschGroup` | Company ID is case-sensitive |
| **Workday** | `https://{tenant}.wd5.myworkdayjobs.com/{Site}` | URL only (needs tenant + site) |
| **Workable** | `https://apply.workable.com/pearltalent` or `pearltalent` | Detail fetched per job |
| **Recruitee** | `https://{company}.recruitee.com` or `{company}` | Full content inline |
| **Breezy** | `https://{company}.breezy.hr` or `{company}` | Description from posting page |
| **Personio** | `https://vodeno.jobs.personio.com` or `vodeno` | Public XML feed |
| **BambooHR** | `https://soundstripe.bamboohr.com` or `soundstripe` | Careers API |
| **iCIMS** | `https://careers-{company}.icims.com/jobs/search` or `{company}` | Detail fetched per job |
| **Oracle Cloud HCM** | `https://{pod}.fa.{dc}.oraclecloud.com/hcmUI/CandidateExperience/en/sites/CX_1` | URL only (needs pod + site) |
| **SAP SuccessFactors** | `https://jobs.{company}.com` (Career Site Builder site) | Paste the career-site URL — auto-detected |
| **UKG Pro (UltiPro)** | `https://recruiting.ultipro.com/{TENANT}/JobBoard/{guid}` | URL only (needs tenant + board id) |
| **ADP Workforce Now** | `https://workforcenow.adp.com/…/recruitment.html?cid={guid}` | URL only (needs career-center id) |
| **Eightfold AI** | `https://{company}.eightfold.ai` or `{company}` | Detail fetched per job |
| **Jobvite** | `https://jobs.jobvite.com/{company}` or `{company}` | Description via JSON-LD |
| **Teamtailor** | `https://{company}.teamtailor.com` or `{company}` | Full content inline (RSS) |
| **Pinpoint** | `https://{company}.pinpointhq.com` or `{company}` | Full content inline |
| **Taleo (Oracle)** | `https://{tenant}.taleo.net/careersection/{n}/jobsearch.ftl` | URL only (needs tenant + section) |
| **Phenom** | `https://careers.{company}.com` (Phenom-powered site) | Paste the career-site URL — auto-detected |
| **iCIMS modern (Jibe)** | `https://careers.{company}.com` (Jibe-powered site) | Paste the career-site URL — auto-detected |
| **BrassRing (IBM Kenexa)** | `https://sjobs.brassring.com/…?partnerid={p}&siteid={s}` | URL only (needs partnerid + siteid) |
| **Comeet (Spark Hire)** | `https://www.comeet.com/jobs/{company}/{uid}` | URL only (needs company uid) |

#### Copy-pasteable startUrls

```json
{
  "startUrls": [
    "https://boards.greenhouse.io/stripe",
    "https://jobs.lever.co/spotify",
    "https://jobs.ashbyhq.com/ramp",
    "https://recruiting.ultipro.com/MUS1002MOMA/JobBoard/9dbfa465-a36a-46c3-b31e-4929830ab266",
    "https://valero.taleo.net/careersection/2/jobsearch.ftl",
    "https://polestar.teamtailor.com",
    "https://jobs.sap.com"
  ]
}
```

#### Company-name mode (ATS auto-detected)

```json
{
  "organizations": ["stripe", "notion", "ramp"]
}
```

#### Spreadsheet mode

```json
{
  "inputUrl": "https://docs.google.com/spreadsheets/d/…/edit"
}
```

Cells may hold board URLs and/or bare company names; the sheet is parsed and merged with `startUrls` / `organizations`. All three modes can be mixed in one run.

#### Unsupported inputs

- ❌ Job boards outside the 24 ATS platforms above (LinkedIn, Indeed, company sites with fully custom career pages)
- ❌ "All companies on Greenhouse" — ATSs publish no public directory of boards; you supply the companies
- ❌ Content behind a login wall or internal/private postings

***

### Use Cases

| Audience | Use case |
|---|---|
| **Sales teams** | Hiring is a buying signal — pull open roles for target accounts and pair each with a contact email for outreach |
| **Recruiting agencies** | Monitor client and prospect boards live; catch new roles the day they open |
| **Job boards & aggregators** | Ingest fresh, structured roles from hundreds of companies without one integration per ATS |
| **Talent-intelligence teams** | Hiring velocity, remote-vs-onsite mix, departments, and pay bands across a company portfolio |
| **RevOps / CRM teams** | Append live hiring activity + a contact address to account lists on a schedule |

***

### How It Works

1. **Input** — provide board URLs, bare company names, a spreadsheet of either, or any mix
2. **Detect** — URLs route to their ATS by hostname; bare names are auto-detected by probing the supported platforms in order
3. **Fetch live** — each company's jobs come from the ATS's own public API or career site at run time (one call for inline ATSs; list + per-job detail for 2-call ATSs like SmartRecruiters, Workday, iCIMS, Oracle Cloud HCM, UKG, ADP)
4. **Normalize & filter** — every job is mapped to one common schema; title/keyword/location/department/remote/date filters run before billing
5. **Enrich & push** (optional) — discover the company website, attach a public contact email, and push one row per job

***

### Input Configuration

#### Input fields

| Field | Type | Default | Description |
|---|---|---|---|
| `startUrls` | array | – | ATS board/job URLs (any of the 22 platforms) |
| `organizations` | array | – | Bare company names/slugs — ATS auto-detected |
| `inputUrl` | string | – | Public URL to an `.xlsx` / `.csv` / Google Sheet of career links or company names — parsed and merged with the lists |
| `enrichEmails` | boolean | `false` | Attach company `contactEmail` + `contactWebsite` (lead-gen) |
| `maxItems` | integer | `5000` | Hard cap on job rows across the whole run — use it to control billing |
| `maxConcurrency` | integer | `10` | Companies processed in parallel |
| `titleKeyword` | string | – | Keep only jobs whose title contains this text (case-insensitive) |
| `keywords` | string | – | Keep only jobs matching these keywords across title, detected skills, and description |
| `location` | string | – | Keep only jobs whose location contains this text, e.g. `remote`, `London` |
| `department` | string | – | Keep only jobs whose department contains this text |
| `employmentType` | string | – | Keep only jobs whose employment type contains this stem, e.g. `full` |
| `language` | string | – | Preferred posting language (Personio returns it natively; SmartRecruiters filtered to it) |
| `remoteOnly` | boolean | `false` | Keep only jobs flagged remote |
| `postedAfter` | string | – | Keep only jobs published on/after `YYYY-MM-DD` — ideal for scheduled monitoring |
| `includeDescription` | boolean | `true` | Off = leaner rows (skips the per-job detail fetch on 2-call ATSs — much faster on big enterprise boards) |
| `includeRawJson` | boolean | `false` | Attach the raw ATS payload under `raw` |
| `proxy` | object | – | Leave empty — traffic already routes through built-in residential proxy at no extra cost |

#### Common scenarios

**1. Lead-gen: engineering roles + contact emails for a target list**

```json
{
  "organizations": ["stripe", "ramp", "vodeno"],
  "enrichEmails": true,
  "titleKeyword": "engineer",
  "maxItems": 1000
}
```

**2. Monitoring: only new roles since yesterday, lean rows**

```json
{
  "startUrls": ["https://boards.greenhouse.io/stripe", "https://jobs.lever.co/spotify"],
  "postedAfter": "2026-07-18",
  "includeDescription": false
}
```

**3. Bulk sheet of career links, remote roles only**

```json
{
  "inputUrl": "https://docs.google.com/spreadsheets/d/…/edit",
  "remoteOnly": true,
  "maxItems": 5000
}
```

***

### Output Overview

Each dataset item is a job row containing:

- **Core fields** — title, department, employment type, workplace type, remote flag
- **Locations** — primary location plus the full locations array
- **Content** — full description as HTML and plain text (toggle with `includeDescription`)
- **Provenance** — source ATS, company, stable `globalId` (`ats:org:jobId`) for cross-run dedup
- **Links & dates** — `jobUrl`, `applyUrl`, `publishedAt`, `scrapedAt`
- **Lead-gen enrichment** — `contactEmail`, `contactWebsite`, `emailEnrichment` (when `enrichEmails` is on)
- **Traceability** — `inputIndex` and `jobsFound` tie each row back to the input that produced it

Rows arrive in the same order as your inputs. All start modes (URL / company name / spreadsheet) resolve to the same job-row shape.

***

### Output Samples

#### Company-name start, with email enrichment

```jsonc
{
  "ats": "greenhouse",
  "org": "stripe",
  "company": "Stripe",
  "globalId": "greenhouse:stripe:7954688",
  "title": "Backend Engineer",
  "department": "Engineering",
  "workplaceType": "Hybrid",
  "isRemote": false,
  "location": "San Francisco, CA",
  "locations": ["San Francisco, CA"],
  "descriptionHtml": "<p>…</p>",
  "descriptionText": "…",
  "publishedAt": "2026-06-19T12:11:02-04:00",
  "jobUrl": "https://stripe.com/jobs/search?gh_jid=7954688",
  "applyUrl": "https://stripe.com/jobs/search?gh_jid=7954688",
  "contactEmail": "careers@stripe.com",
  "contactWebsite": "stripe.com",
  "emailEnrichment": { "website": "stripe.com", "emails": ["careers@stripe.com"] },
  "jobsFound": 495,
  "inputIndex": 0,
  "scrapedAt": "2026-06-20T00:00:00.000Z"
}
```

***

### Key Output Fields

#### Job Core

- `title`, `department`, `employmentType`, `workplaceType`, `isRemote`

#### Locations

- `location` (primary), `locations[]` (all)

#### Content

- `descriptionHtml`, `descriptionText` (present when `includeDescription` is on)
- `raw` (original ATS payload, when `includeRawJson` is on)

#### Provenance & Dedup

- `ats` (`greenhouse` / `lever` / `ashby` / `smartrecruiters` / `workday` / `workable` / `recruitee` / `breezy` / `personio` / `bamboohr` / `icims` / `jibe` / `oraclecloud` / `taleo` / `successfactors` / `ukg` / `adp` / `eightfold` / `phenom` / `jobvite` / `teamtailor` / `pinpoint` / `brassring` / `comeet`)
- `org`, `company`, `globalId` (stable `ats:org:jobId` — dedup across runs)

#### Links & Dates

- `jobUrl`, `applyUrl`, `publishedAt`, `scrapedAt`

#### Lead-Gen Enrichment (when `enrichEmails: true`)

- `contactEmail`, `contactWebsite`, `emailEnrichment {website, emails[]}`

#### Run Traceability

- `inputIndex`, `jobsFound`

***

### FAQ

#### How is this different from a job-database API?

Database-style actors serve rows from an index they crawled earlier — coverage is limited to companies already in the index, and freshness depends on their crawl cycle. This actor has no index: it calls each company's ATS live during your run, so any company on a supported platform is covered, and every row reflects the board's state right now.

#### How does ATS auto-detection work?

For a bare company name, the actor tries each supported ATS in order and uses the first that returns jobs. For a URL, it routes by hostname; a company's own careers page (e.g. `jobs.acme.com`) is fetched and sniffed for the ATS it embeds — that's also how SAP SuccessFactors career sites are recognized. SmartRecruiters company IDs are case-sensitive — pass the exact ID or a careers URL.

#### Which platforms need a URL instead of a company name?

Workday, Oracle Cloud HCM, Taleo, UKG Pro (UltiPro), ADP Workforce Now, SAP SuccessFactors, Phenom, iCIMS modern (Jibe), BrassRing, and Comeet/Spark Hire sites embed tenant identifiers in their URLs (or live on company domains) that can't be guessed from a company name — paste the board or career-site URL for those. Everything else works from a bare name.

#### Can it scrape every company on an ATS?

No — ATS platforms publish no public directory of all boards. You supply the companies (names, URLs, or a spreadsheet). That's the flip side of live coverage: any company you name is reachable, but there's no "give me everything" mode.

#### How accurate is the contact email?

Best-effort. It discovers the company website and harvests publicly published addresses (typically general inboxes like `careers@` / `info@`); many companies publish none, so coverage varies. Off by default since it adds time and cost.

#### How do I see which inputs worked?

Every run saves an `INPUT-STATUS` record to the run's key-value store — one entry per input with its detected ATS, `status` (ok / failed), and `jobsFound`. Open it (run → Storage → Key-value store) to spot inputs that returned nothing.

#### Can I get postings in English?

Set `language` to `en`. Personio returns English descriptions natively, and SmartRecruiters jobs are filtered to English. Other ATSs return whatever language the role was posted in — they aren't translated.

#### Do I need a proxy?

No. These ATS endpoints are public, and the actor already routes traffic through its own built-in residential proxy at no extra cost. Leave the `proxy` field empty.

#### Can I scrape private or internal postings?

No. The actor only reads publicly available postings from each ATS's public endpoints. Anything behind a login wall is out of scope.

***

### Support

Found a bug or have a feature request? Open an issue on the actor's [Issues tab](https://apify.com/memo23/career-site-ats-jobs-api/issues) — issues are typically answered within a day.

### Additional Services

Need another ATS platform added, a custom export shape, or a scheduled monitoring pipeline delivered to your CRM? I do tailored work — open an issue or reach out via the actor's contact form.

### Explore More Scrapers

If you found this useful, you might also like:

- [**Glassdoor Scraper**](https://apify.com/memo23/glassdoor-scraper-ppr) — company reviews, salaries, and interviews
- [**Indeed Scraper**](https://apify.com/memo23/apify-indeed-cheerio-ppr) — job listings from Indeed across countries
- [**LinkedIn Jobs Scraper**](https://apify.com/memo23/linkedin-jobs-scraper) — public LinkedIn job search results
- [**Wellfound Jobs Scraper**](https://apify.com/memo23/wellfound-jobs-scraper) — startup jobs with company and funding context

Full list at [apify.com/memo23](https://apify.com/memo23).

***

### 🤖 For AI Agents & LLM Apps

Compact reference for AI agents calling this actor via the [Apify MCP server](https://mcp.apify.com) or the Apify API (actor: `memo23/career-site-ats-jobs-api`).

**Purpose:** fetch live job postings for named companies across 24 ATS platforms (Greenhouse, Lever, Ashby, SmartRecruiters, Workday, Workable, Recruitee, Breezy, Personio, BambooHR, iCIMS classic & modern/Jibe, Oracle Cloud HCM, Taleo, SAP SuccessFactors, UKG Pro/UltiPro, ADP Workforce Now, Eightfold AI, Phenom, Jobvite, Teamtailor, Pinpoint, BrassRing/IBM Kenexa, Comeet/Spark Hire), normalized to one schema, optionally with a public company contact email.

**Minimal input:**

```json
{ "organizations": ["stripe"], "maxItems": 25 }
```

**Output:** one dataset row per job — `ats`, `org`, `company`, `globalId`, `title`, `department`, `employmentType`, `workplaceType`, `isRemote`, `location`, `locations[]`, `descriptionHtml`, `descriptionText`, `publishedAt`, `jobUrl`, `applyUrl`, `jobsFound`, `inputIndex`, `scrapedAt`, plus `contactEmail` / `contactWebsite` / `emailEnrichment {website, emails[]}` when `enrichEmails` is true.

**Behaviors an agent should know:**

- Always set `maxItems` — a large company list can return tens of thousands of rows; the default cap is 5000.
- Input modes merge: `startUrls` (board URLs), `organizations` (bare names, ATS auto-detected), and `inputUrl` (sheet of either) can be combined in one run.
- Filters (`titleKeyword`, `keywords`, `location`, `department`, `employmentType`, `remoteOnly`, `postedAfter`) apply **before** billing — filtered-out jobs are never charged.
- Billing: charged per job row pushed (primary event) plus a one-time actor-start event; `enrichEmails` adds a small per-company additional-data charge.
- There is no "all companies" mode — the agent must supply company names or board URLs. Per-input success is reported in the run's `INPUT-STATUS` key-value record.
- Workday, Oracle Cloud HCM, Taleo, UKG Pro, ADP, SAP SuccessFactors, Phenom, iCIMS modern (Jibe), BrassRing, and Comeet/Spark Hire sites need a board/career-site URL (tenant ids aren't guessable from a name); the other platforms accept bare company names.
- No proxy configuration needed; leave `proxy` empty.

***

### ⚠️ Disclaimer

This Actor is an independent tool and is not affiliated with, endorsed by, or sponsored by Greenhouse Software, Inc., Lever (Employ Inc.), Ashby Inc., SmartRecruiters Inc., Workday, Inc., Workable Software Ltd., Recruitee B.V., Breezy HR, Inc., Personio SE & Co. KG, BambooHR LLC, iCIMS, Inc., Oracle Corporation (Oracle Cloud HCM, Taleo), SAP SE, UKG Inc., ADP, Inc., Eightfold AI Inc., Phenom People, Inc., Jobvite (Employ Inc.), Teamtailor AB, Pinpoint Software Ltd., IBM Corporation (BrassRing/Kenexa), Spark Hire, Inc. (Comeet), or any of the companies whose job boards it reads. All trademarks mentioned are the property of their respective owners.

The scraper accesses only publicly available job-posting pages and endpoints exposed by each ATS's own public career-site interface, plus publicly published company contact details — no authenticated endpoints, paid features, or content behind any login wall. Users are responsible for ensuring their use complies with each platform's Terms of Service, applicable data-protection law (GDPR, CCPA, etc.), and any contractual obligations of their own organization — especially when using contact data for outreach.

***

### SEO Keywords

ats jobs scraper, multi-ats scraper, career site jobs api, career site job listing api, scrape greenhouse jobs, lever jobs scraper, ashby jobs scraper, workday jobs scraper, smartrecruiters scraper, icims jobs scraper, taleo jobs scraper, oracle taleo scraper, oracle cloud hcm scraper, oracle recruiting cloud scraper, sap successfactors jobs scraper, ultipro jobs scraper, ukg pro scraper, adp workforce now jobs scraper, eightfold ai scraper, phenom people scraper, jibe career site scraper, jobvite scraper, teamtailor scraper, pinpoint ats scraper, brassring scraper, ibm kenexa scraper, comeet scraper, spark hire scraper, applicant tracking system scraper, company career page scraper, live job listings api, job database alternative, hiring signal data, recruiting lead generation, jobs with contact email, sales intelligence hiring data, job market monitoring, talent intelligence data, hr tech data extraction

# Actor input Schema

## `startUrls` (type: `array`):

Any supported ATS board or job URL — Greenhouse (boards.greenhouse.io/{org}), Lever (jobs.lever.co/{org}), Ashby (jobs.ashbyhq.com/{org}), SmartRecruiters (careers.smartrecruiters.com/{Id}), Workday ({tenant}.wdN.myworkdayjobs.com/{Site}), Workable (apply.workable.com/{slug}), Recruitee ({slug}.recruitee.com), Breezy ({slug}.breezy.hr), Personio ({co}.jobs.personio.com), BambooHR ({co}.bamboohr.com), iCIMS (careers-{co}.icims.com), Oracle Cloud HCM ({pod}.fa.{dc}.oraclecloud.com/hcmUI/…/sites/CX\_n), UKG Pro (recruiting.ultipro.com/{TENANT}/JobBoard/{id}), ADP Workforce Now (workforcenow.adp.com/…recruitment.html?cid={id}), Eightfold ({co}.eightfold.ai), Jobvite (jobs.jobvite.com/{co}), Teamtailor ({co}.teamtailor.com), Pinpoint ({co}.pinpointhq.com), Taleo ({tenant}.taleo.net/careersection/{n}/jobsearch.ftl), BrassRing (sjobs.brassring.com/…?partnerid={p}\&siteid={s}), Comeet/Spark Hire (comeet.com/jobs/{co}/{uid}), plus career sites on company domains that are auto-detected when you paste their URL: SAP SuccessFactors (jobs.{company}.com), Phenom (careers.{company}.com), and iCIMS modern/Jibe sites. The ATS is detected from the URL host.

## `organizations` (type: `array`):

Bare company names/slugs, e.g. \["stripe", "notion", "ramp"]. For each, the actor probes the supported ATSs and uses the one that hosts that company. (SmartRecruiters company IDs are case-sensitive — pass the exact ID or a careers URL.)

## `greenhouseBoards` (type: `array`):

Greenhouse board tokens — the last part of boards.greenhouse.io/{token}. Example: \["stripe", "databricks"].

## `leverCompanies` (type: `array`):

Lever org slugs — the part after jobs.lever.co/. Example: \["spotify", "voodoo"].

## `ashbyOrgs` (type: `array`):

Ashby org slugs — the part after jobs.ashbyhq.com/. Example: \["ramp", "notion"].

## `smartrecruitersCompanies` (type: `array`):

SmartRecruiters company IDs, exactly as they appear after smartrecruiters.com/. CASE-SENSITIVE: "BoschGroup" works, "boschgroup" returns nothing. Example: \["BoschGroup", "Visa"].

## `inputUrl` (type: `string`):

Optional. A public URL to an .xlsx, .csv, or Google Sheet whose cells hold ATS board URLs and/or company names. Every URL or slug found is scraped (ATS auto-detected). For Google Sheets, share as 'Anyone with the link'.

## `enrichEmails` (type: `boolean`):

When enabled, each company is enriched with a contact email by discovering its website (Clearbit) and harvesting contact details — adds `contactEmail`, `contactWebsite`, and a detailed `emailEnrichment` object to every row. Best-effort. Adds time/cost per company, so it's off by default.

## `qualifyByPayment` (type: `boolean`):

Requires "Enrich with company contact email". Scans each company's website — reusing the pages already fetched for email discovery, so no extra requests or time — for payment processors and e-commerce platforms (Stripe, Shopify, PayPal, Paddle, Lemon Squeezy, WooCommerce, Square, Chargebee and more). Adds takesPayments, paymentProcessors, stripeLiveKey and paymentConfidence, turning raw contacts into monetization-qualified leads.

## `maxItems` (type: `integer`):

Cap on job rows emitted for EACH company/URL you provide. 3 companies x 200 = up to 600 rows. To bound the whole run regardless of how many companies you pass, set 'Maximum jobs for the whole run' below — that one is the global cap.

## `maxItemsTotal` (type: `integer`):

Hard ceiling on job rows across the entire run, all companies combined — the safest way to control billing when you paste a long list of companies. Leave empty for no global cap (the per-company cap above still applies). Free-plan runs are capped at 100 rows regardless.

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

How many companies to process in parallel.

## `titleKeyword` (type: `string`):

Keep only jobs whose title contains this text (case-insensitive), e.g. "engineer".

## `keywords` (type: `string`):

Keep only jobs matching these keywords across the title, detected skills AND the description. Space-separated terms are ALL required (AND), e.g. "python kubernetes" keeps jobs mentioning both — even when a skill appears only in the job body. Broader than "Title contains".

## `location` (type: `string`):

Keep only jobs whose location contains this text, e.g. "remote", "London", "New York".

## `department` (type: `string`):

Keep only jobs whose department contains this text, e.g. "Engineering".

## `employmentType` (type: `string`):

Keep only jobs whose employment type contains this text. Use a short stem that works across all ATSs, e.g. "full", "part", "contract", "intern".

## `language` (type: `string`):

Preferred posting language. Personio returns this language's descriptions (English by default); SmartRecruiters jobs are filtered to it. Other ATSs return whatever language the role was posted in.

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

Keep only jobs flagged remote.

## `postedAfter` (type: `string`):

Keep only jobs published on or after this date (YYYY-MM-DD). Ideal for scheduled monitoring runs that only want fresh roles.

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

Include `descriptionHtml` and `descriptionText` on each row. Turn off for leaner, cheaper rows when you only need title/location/links. (For 2-call ATSs — SmartRecruiters, BambooHR — turning this off also skips the per-job detail fetch, making big runs much faster.)

## `excludeBoilerplate` (type: `boolean`):

SmartRecruiters only. Drops the "Company Description" and "Additional Information" sections from the combined `descriptionHtml`/`descriptionText`, keeping just the role and its qualifications. All four sections are emitted as separate columns (`companyDescription`, `jobDescription`, `qualifications`, `additionalInformation`) either way.

## `includeQuestions` (type: `boolean`):

Greenhouse only. Also fetches each job's application-form questions via one extra API call PER JOB — slower and far more requests, so it's off by default. Adds `questions` + `questionCount`.

## `translateToEnglish` (type: `boolean`):

Companies post in whatever language they hire in, and one board can mix several. Enable this to auto-translate non-English titles/descriptions to English — free, no API key needed. Originals are kept in `titleOriginal`/`descriptionTextOriginal`, plus `detectedLanguage` + `translated` flags. Only non-English rows are translated. Works best with 'Include full job description' ON.

## `includeRawJson` (type: `boolean`):

Attach the original, unmodified ATS job object to each row under a `raw` field. Off by default to keep rows lean.

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

Any name you choose, e.g. "my-ats-watch". Job state is stored under it in a named key-value store and reused by every run that passes the same name, keyed on each job's `globalId`. Each row then carries `changeStatus` (new / updated / unchanged) and `firstSeenAt`; run totals land in the RUN-SUMMARY record. Leave blank to disable. Note: a job counts as "updated" when its title, location, employment type, salary, dates or description text changed — so keep 'Include full job description' stable between runs, or the first run after a flip marks everything updated.

## `onlyNewOrUpdated` (type: `boolean`):

Requires a state name above. Drops `unchanged` jobs from the output entirely, so a daily monitoring run returns (and bills for) just the delta rather than the whole board. The unchanged jobs are still tracked in state — they simply aren't emitted. Note: 'new' means 'not yet seen under this state name', not 'newly posted'. Keep the per-company cap at or above the board size, otherwise each run skips the jobs it already knows and back-fills with deeper ones it has not reached yet, which report as new.

## `proxy` (type: `object`):

Leave empty — the actor already routes all traffic through its own built-in residential proxy at no extra cost to you. Only set this if you want to use your own proxies.

## Actor input object example

```json
{
  "startUrls": [
    "https://boards.greenhouse.io/lattice",
    "https://jobs.lever.co/spotify",
    "https://jobs.ashbyhq.com/ramp",
    "https://careers.smartrecruiters.com/BoschGroup",
    "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite",
    "https://vodeno.jobs.personio.com",
    "https://soundstripe.bamboohr.com"
  ],
  "organizations": [
    "notion",
    "figma"
  ],
  "greenhouseBoards": [],
  "leverCompanies": [],
  "ashbyOrgs": [],
  "smartrecruitersCompanies": [],
  "enrichEmails": false,
  "qualifyByPayment": false,
  "maxItems": 5000,
  "maxConcurrency": 10,
  "remoteOnly": false,
  "includeDescription": true,
  "excludeBoilerplate": false,
  "includeQuestions": false,
  "translateToEnglish": false,
  "includeRawJson": false,
  "onlyNewOrUpdated": false
}
```

# Actor output Schema

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

Every scraped job row in the default dataset.

## `inputStatus` (type: `string`):

One entry per input link/company: which ATS was detected, how many jobs it found, how many rows it emitted, and the error when an input yielded nothing.

## `inputStatusCsv` (type: `string`):

The same per-input report as a CSV file, for opening directly in Excel.

## `runSummary` (type: `string`):

Run totals plus the per-input report — including the new / updated / unchanged counts when change detection is on.

# 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 = {
    "startUrls": [
        "https://boards.greenhouse.io/lattice",
        "https://jobs.lever.co/spotify",
        "https://jobs.ashbyhq.com/ramp",
        "https://careers.smartrecruiters.com/BoschGroup",
        "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite",
        "https://vodeno.jobs.personio.com",
        "https://soundstripe.bamboohr.com"
    ],
    "organizations": [
        "notion",
        "figma"
    ],
    "greenhouseBoards": [],
    "leverCompanies": [],
    "ashbyOrgs": [],
    "smartrecruitersCompanies": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("memo23/career-site-ats-jobs-api").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 = {
    "startUrls": [
        "https://boards.greenhouse.io/lattice",
        "https://jobs.lever.co/spotify",
        "https://jobs.ashbyhq.com/ramp",
        "https://careers.smartrecruiters.com/BoschGroup",
        "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite",
        "https://vodeno.jobs.personio.com",
        "https://soundstripe.bamboohr.com",
    ],
    "organizations": [
        "notion",
        "figma",
    ],
    "greenhouseBoards": [],
    "leverCompanies": [],
    "ashbyOrgs": [],
    "smartrecruitersCompanies": [],
}

# Run the Actor and wait for it to finish
run = client.actor("memo23/career-site-ats-jobs-api").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 '{
  "startUrls": [
    "https://boards.greenhouse.io/lattice",
    "https://jobs.lever.co/spotify",
    "https://jobs.ashbyhq.com/ramp",
    "https://careers.smartrecruiters.com/BoschGroup",
    "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite",
    "https://vodeno.jobs.personio.com",
    "https://soundstripe.bamboohr.com"
  ],
  "organizations": [
    "notion",
    "figma"
  ],
  "greenhouseBoards": [],
  "leverCompanies": [],
  "ashbyOrgs": [],
  "smartrecruitersCompanies": []
}' |
apify call memo23/career-site-ats-jobs-api --silent --output-dataset

```

## MCP server setup

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

```

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/aypv72AiEsNAm9xjN/builds/XY4J0NckGbrMW04AP/openapi.json
