# Workday, Greenhouse, Lever & Ashby Jobs Scraper (`oldjard/ats-career-site-jobs`) Actor

Scrape every open job from company career sites on Workday, Greenhouse, Lever and Ashby. Paste career-site URLs; the ATS is auto-detected. Full descriptions, locations, remote flag, salary, posting dates, and an only-new-jobs mode for daily monitoring. $3 per 1,000 jobs.

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

## Pricing

Pay per event

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

## Workday, Greenhouse, Lever & Ashby Jobs Scraper

**Scrape every open job from company career sites** on **Workday, Greenhouse, Lever and Ashby** in one run and one
output format. Paste career-site URLs; the actor detects the applicant tracking system (ATS) and reads the board's
own public job feed, live, not from a stale database. Use it as a **Workday jobs API**, a **Greenhouse, Lever or
Ashby jobs scraper**, or a daily **new-jobs feed** for job boards, recruiting and sales tools.

**Try it in one click:** the input is prefilled with Ramp, Airbnb and Mastercard (20 jobs each). Apify's free plan
covers about 1,600 jobs a month.

### What job data do you get?

- **All open jobs, even on huge Workday sites.** Workday's search stops at 2,000 results; this actor splits big sites
  by Workday's own filters until every job is reachable. Tested on a 14,100-job Workday site (listing mode): 99.9% of
  jobs found. Counts are checked against each board's own total.
- **Full job descriptions** (HTML and clean text), title, locations, remote flag, workplace and employment type,
  department, team, posting date, **salary** when the board publishes it, job and requisition IDs, apply link.
- **Only new jobs since the last run:** schedule it daily and get (and pay for) only new postings.
- **Filters:** title keywords (whole words, so `java` ≠ `javascript`), excluded words, locations, remote only,
  posted within N days, a per-company cap.

### How to scrape Workday, Greenhouse, Lever and Ashby jobs in 3 steps

1. Add career-site URLs, one per line (formats below). A company's own careers page works when it embeds a board.
2. Optional: set filters, **Max jobs per company**, or turn on **Only new jobs**.
3. Click **Start**, then download JSON, CSV or Excel, or read it through the API.

### How much does it cost to scrape jobs?

**$3 per 1,000 jobs** returned, so Apify's $5 monthly free credit covers about 1,600 jobs. Sites that fail cost nothing, and in only-new mode jobs you've already seen are free.
A daily feed of 10 companies with 20 new jobs a day between them costs about $1.80 a month after the first full run.

### Supported URL formats

| ATS | Examples |
|---|---|
| Workday | `https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite`, `https://nvidia.wd5.myworkdayjobs.com/en-US/NVIDIAExternalCareerSite/job/...`, `https://wd3.myworkdaysite.com/recruiting/acme/Careers` |
| Greenhouse | `https://job-boards.greenhouse.io/airbnb`, `https://boards.greenhouse.io/airbnb`, `https://boards.greenhouse.io/embed/job_board?for=airbnb`, or `greenhouse:airbnb` |
| Lever | `https://jobs.lever.co/palantir`, `https://jobs.eu.lever.co/acme`, or `lever:palantir` |
| Ashby | `https://jobs.ashbyhq.com/ramp`, or `ashby:ramp` |
| Company careers page | Any page that embeds or links one of the boards above, e.g. `https://vercel.com/careers` or `https://linear.app/careers` |

The vendor's own site with a company name after it (`greenhouse.io/airbnb`, `lever.co/palantir`, `ashbyhq.com/ramp`)
is read as that company's board. A bare company name such as `airbnb` is not an address; paste its careers page or
board URL instead.

A Workday URL needs the site name after the host (the part after `myworkdayjobs.com/`). Open the company's job list
in your browser and copy that address.

### Input example

```json
{
    "startUrls": [
        "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite",
        "https://job-boards.greenhouse.io/airbnb",
        "https://jobs.lever.co/palantir",
        "https://jobs.ashbyhq.com/ramp"
    ],
    "keywords": ["engineer", "data scientist"],
    "locations": ["United States", "Remote"],
    "postedWithinDays": 14,
    "onlyNew": false
}
```

### Output example

One item per job. `descriptionHtml` and `descriptionText` are shortened here.

```json
{
    "title": "Security Engineer, Cloud",
    "company": "Ramp",
    "ats": "ashby",
    "location": "New York, NY (HQ)",
    "locations": ["New York, NY (HQ)", "Remote (Canada)", "Remote (US)", "Miami, FL"],
    "country": "USA",
    "isRemote": true,
    "workplaceType": "Hybrid",
    "employmentType": "Full-time",
    "department": "Engineering",
    "team": "Backend",
    "postedAt": "2026-04-07T17:12:35.753Z",
    "postedText": null,
    "updatedAt": null,
    "jobId": "34413f8d-26bf-4bbc-8ade-eb309a0e2245",
    "requisitionId": null,
    "salary": "$211.4K - $290.6K",
    "jobUrl": "https://jobs.ashbyhq.com/ramp/34413f8d-26bf-4bbc-8ade-eb309a0e2245",
    "applyUrl": "https://jobs.ashbyhq.com/ramp/34413f8d-26bf-4bbc-8ade-eb309a0e2245/application",
    "descriptionHtml": "<h1><strong>About Ramp</strong></h1><p>Ramp is building ...</p>",
    "descriptionText": "ABOUT RAMP\n\nRamp is building ...",
    "companyBoard": "https://jobs.ashbyhq.com/ramp",
    "sourceUrl": "https://jobs.ashbyhq.com/ramp",
    "scrapedAt": "2026-10-05T16:09:31.011Z"
}
```

| Field | Meaning |
|---|---|
| `isRemote` | `true` if the board marks the job remote or a location says "remote"; `false` if it is marked on-site or hybrid; `null` if the board does not say. |
| `workplaceType` | The board's own wording (`Remote`, `Hybrid`, `On-site`, Workday's `remoteType` such as `Office - Flexible`). |
| `employmentType` | Normalized to `Full-time`, `Part-time`, `Contract`, `Internship` or `Temporary`; other values pass through. |
| `postedAt` | ISO date the job was published. On Workday, the posting start date. |
| `postedText` | Workday's own wording, e.g. "Posted 3 Days Ago". |
| `salary` | Only when the board publishes pay (common on Ashby and Lever; Greenhouse only when the company adds it as a custom field). |
| `company` | The board's company name. Workday and Ashby do not publish one, so it comes from the board's address (e.g. `nvidia` becomes `Nvidia`). |

What each ATS provides differs: Workday has no department field, Greenhouse has no country field, and so on.
Missing values are `null`, never guessed.

#### Run summary

Each run writes an `OUTPUT` record (in the Console: **Storage → Key-value store**): jobs per site, sites that failed
and why. A site that fails (wrong URL, domain that does not resolve, board removed) does not stop the others and costs
nothing.

### Only new jobs (monitoring)

Turn on **Only new jobs since the last run** and schedule the actor (for example daily). The first run returns all
current jobs and remembers them. Later runs return only jobs that appeared since. Memory is kept per **state key**,
so you can run several independent monitors. On Workday, jobs already seen are not fetched again, which makes daily
runs fast.

### Speed and limits

- Greenhouse, Lever and Ashby return a whole board in one or a few requests: seconds per company.
- Workday needs one request per job for the full description. A 1,000-job Workday site takes about 5 minutes
  on Apify (1,030 jobs in 300 s).
  Turn off **Include full job descriptions** to list Workday jobs 20 at a time instead (much faster, but no
  descriptions, exact dates or full location lists).
- Requests are polite: a few per second per site, with retries on errors, and `Crawl-delay` is honored.

### Responsible use

The actor reads only public job listings that companies publish so job seekers can find them. It follows each
site's `robots.txt` and skips sites that disallow access. It needs no login, and it does not collect personal
data. A recruiter's name appears only if the company wrote it into the job text. SmartRecruiters is not supported
because its API's robots.txt disallows automated access. Use the data in line with the law where you are and the
sites' terms.

### Ready-made examples

Each one opens this actor with the input already filled in. Click **Try** to run it, or change the input to fit your own list.

- [NVIDIA open jobs (Workday career site)](https://apify.com/oldjard/ats-career-site-jobs/examples/nvidia-jobs-workday)
- [OpenAI open jobs (Ashby job board)](https://apify.com/oldjard/ats-career-site-jobs/examples/openai-jobs-ashby)
- [Anthropic open jobs (Greenhouse board)](https://apify.com/oldjard/ats-career-site-jobs/examples/anthropic-jobs-greenhouse)
- [Remote software engineer jobs at top startups](https://apify.com/oldjard/ats-career-site-jobs/examples/remote-software-engineer-jobs-startups)
- [Jobs posted in the last 7 days at selected companies](https://apify.com/oldjard/ats-career-site-jobs/examples/new-jobs-posted-last-7-days)
- [Data and analytics jobs in London](https://apify.com/oldjard/ats-career-site-jobs/examples/data-jobs-in-london)
- [Palantir open jobs (Lever job board)](https://apify.com/oldjard/ats-career-site-jobs/examples/palantir-jobs-lever)

### More tools from oldjard

- [Tech Stack Detector](https://apify.com/oldjard/tech-stack-detector): what any list of websites is built with.
- [Sitemap URL Extractor](https://apify.com/oldjard/sitemap-url-extractor): every URL on a website, for RAG and SEO.
- [Shopify Products Scraper & Price Monitor](https://apify.com/oldjard/shopify-products-price-monitor): catalogs and price changes from any Shopify store.
- [Bulk Website Screenshot & URL to PDF](https://apify.com/oldjard/screenshot-pdf): screenshots and PDFs of any list of pages.
- [AI Web Scraper (your own key)](https://apify.com/oldjard/ai-web-scraper): describe fields in English, get JSON.
- [Website Change Monitor](https://apify.com/oldjard/website-change-monitor): a before/after diff by webhook, Slack or Discord when a page changes.
- [Company Registry Lookup](https://apify.com/oldjard/company-registry-lookup): UK Companies House, Spain, France, Finland and Norway in one schema.
- [UK & EU Public Tenders](https://apify.com/oldjard/uk-eu-public-tenders): Find a Tender and TED notices in one table, with daily only-new alerts.

### Use it from an AI agent or the API

- **Minimal input:** `{"startUrls": ["https://jobs.ashbyhq.com/ramp"], "maxJobsPerCompany": 20}`. Set
  `maxJobsPerCompany` to cap the work and the cost.
- **Cost:** $0.003 per job returned. Boards that fail or aren't found are free. 20 jobs ≈ $0.06.
- **Run time (our runs):** 4–9 s for up to 60 jobs from a few boards; about 5 minutes for a whole 1,030-job Workday
  site.
- **Results:** the default dataset, one row per job; per-site status is the `OUTPUT` record in the key-value store.
- Works over the Apify MCP server (`search-actors`, then `call-actor`) and is eligible for agentic payments (x402).

### FAQ

**Is there a Workday jobs API?** This actor reads the same public job-search feed the career site itself uses, so it
works like one, with no key or account.

**Do I need an ATS account or API key?** No.

**My company's careers page isn't detected.** Some sites build their job list with JavaScript after the page loads.
Open one job, copy the address of the Workday/Greenhouse/Lever/Ashby page it links to, and use that.

**Why do I get fewer Workday jobs than the site's "jobs found" number?** That should not happen: the actor checks
its count against the site's own totals and logs a warning if it falls short. Please open an issue with the URL.

**Can it scrape LinkedIn, Indeed or Glassdoor?** No. It reads companies' own career sites only.

### Changelog

- 0.1: first release. Workday, Greenhouse, Lever and Ashby; auto-detection; incremental mode; filters.

# Actor input Schema

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

Company career-site or job-board URLs, one per line. Supported: Workday (https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite), Greenhouse (https://job-boards.greenhouse.io/airbnb), Lever (https://jobs.lever.co/palantir), Ashby (https://jobs.ashbyhq.com/ramp). A company's own careers page also works when it embeds or links one of these boards. Shorthand like greenhouse:airbnb, lever:palantir or ashby:ramp is accepted.

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

Stop after this many jobs from each career site. 0 or empty means no limit (every open job).

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

Return each job's full description as HTML and plain text. On Workday, turning this off is much faster (one request per 20 jobs instead of one per job), but jobs then lack descriptions, exact posting dates, employment type and the full list of locations.

## `onlyNew` (type: `boolean`):

Output only jobs not returned by an earlier run with the same state key. The first run returns all jobs and remembers them. Ideal for a daily schedule: you pay only for new postings.

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

Name of the memory used by 'Only new jobs'. Use a different key for each separate monitor (for example 'fintech-daily'). Runs sharing a key share memory.

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

Keep only jobs whose title contains at least one of these words or phrases (case-insensitive, whole words: 'java' does not match 'javascript'). Empty keeps all.

## `keywordsMatchDescription` (type: `boolean`):

Also keep jobs whose description (not just title) contains a keyword.

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

Drop jobs whose title contains any of these words (for example 'intern', 'senior').

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

Keep only jobs with a location containing one of these texts (for example 'London', 'California', 'Germany'). 'Remote' also matches jobs flagged remote. Empty keeps all.

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

Keep only jobs the career site marks as remote, or whose location says remote.

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

Keep only jobs posted in the last N days. Jobs with no known posting date are kept.

## `failOnSiteError` (type: `boolean`):

Mark the whole run as failed when any career site errors, or returns no jobs while no filters are set. Useful for scheduled health checks and data pipelines. Off by default: failed sites are listed in the OUTPUT record and the run still succeeds.

## Actor input object example

```json
{
  "startUrls": [
    "https://jobs.ashbyhq.com/ramp",
    "https://job-boards.greenhouse.io/airbnb",
    "https://mastercard.wd1.myworkdayjobs.com/CorporateCareers"
  ],
  "maxJobsPerCompany": 20,
  "includeDescription": true,
  "onlyNew": false,
  "stateKey": "default",
  "keywordsMatchDescription": false,
  "remoteOnly": false,
  "failOnSiteError": false
}
```

# Actor output Schema

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

Dataset, one row per job: title, company, ats, location, locations, country, isRemote, workplaceType, employmentType, department, team, postedAt, salary (when published), descriptionText, descriptionHtml, jobId, requisitionId, jobUrl, applyUrl, companyBoard, scrapedAt.

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

Run summary (JSON): jobs output, sites requested and failed, and per site the ATS detected, jobs listed on the board vs jobs output, status and a message.

# 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://jobs.ashbyhq.com/ramp",
        "https://job-boards.greenhouse.io/airbnb",
        "https://mastercard.wd1.myworkdayjobs.com/CorporateCareers"
    ],
    "maxJobsPerCompany": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("oldjard/ats-career-site-jobs").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://jobs.ashbyhq.com/ramp",
        "https://job-boards.greenhouse.io/airbnb",
        "https://mastercard.wd1.myworkdayjobs.com/CorporateCareers",
    ],
    "maxJobsPerCompany": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("oldjard/ats-career-site-jobs").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://jobs.ashbyhq.com/ramp",
    "https://job-boards.greenhouse.io/airbnb",
    "https://mastercard.wd1.myworkdayjobs.com/CorporateCareers"
  ],
  "maxJobsPerCompany": 20
}' |
apify call oldjard/ats-career-site-jobs --silent --output-dataset

```

## MCP server setup

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

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/wWFqpCfHncYaWK8mk/builds/tVj2MpULxOAKazdxc/openapi.json
