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

Open jobs from company career sites on Greenhouse, Lever, Ashby, Workday and 4 more ATS, in one schema. Monitor new jobs; optional AI fields. Pay per job.

- **URL**: https://apify.com/fetchwise/career-site-jobs-scraper.md
- **Developed by:** [kelvin lab](https://apify.com/fetchwise) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.80 / 1,000 job founds

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

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

## What's an Apify Actor?

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

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

## How to integrate an Actor?

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

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

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

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

# README

## Career Site Jobs Scraper: Greenhouse, Lever, Ashby, Workday

Get every open job of the companies you choose, live from their own career sites: Greenhouse, Lever, Ashby, Workday, SmartRecruiters, Workable, Recruitee and Personio, in one clean schema whatever the system. Give job board links, company websites or board names. Run it once for an export, or on a schedule as a monitor that returns only new jobs and tells you which ones closed. Optional AI fields (skills, job function, seniority, visa sponsorship, remote regions, salary from the text and a short summary) come from Claude. Public APIs only: no browser, no login, no residential proxy. You pay per job.

### What you can use it for

- **Job boards and aggregators:** fill a niche board from hundreds of companies and keep it fresh; closed jobs come as free rows so you can delist them.
- **Sales signals:** watch your target accounts and get "Acme opened 3 sales roles in Germany" in Slack, with `input` and `companyDomain` on every row to join back to your CRM.
- **Recruiters and agencies:** new roles at the companies you follow, filtered by title, location, workplace type or department.
- **Labour-market research:** salaries with their source (board, description text or AI), seniority, skills and countries across companies.
- **AI agents:** every field is described for agents that call the Actor through MCP.

### Input

Only `companies` is required.

| Field | Description |
|---|---|
| `companies` | One company per line. Best is its job board link, e.g. `https://boards.greenhouse.io/stripe`, `https://jobs.lever.co/palantir`, `https://jobs.ashbyhq.com/ramp`, `https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite`, `https://careers.smartrecruiters.com/BoschGroup`, `https://apply.workable.com/huggingface`, `https://bunq.recruitee.com`, `https://ottonova.jobs.personio.de`. Also accepted: `greenhouse:stripe`; a company website such as `stripe.com` (its careers page must link to one of these boards); or a bare board name such as `stripe`, tried on Greenhouse, Lever, Ashby, Workable, Recruitee and Personio. Up to 500 per run. |
| `titleKeywords`, `excludeTitleKeywords` | Keep or drop jobs by words in the title (whole words, any case). |
| `locations` | City, US state, country name or two-letter code. |
| `workplaceTypes` | `remote`, `hybrid`, `onsite`. |
| `employmentTypes` | `full_time`, `part_time`, `contract`, `temporary`, `internship`. |
| `departments` | Text in the department or team, e.g. `Engineering`. |
| `postedWithinDays` | Only jobs posted in the last N days. |
| `onlyWithSalary` | Only jobs with a salary from the board or the description text. |
| `descriptionFormat` | `text` (default), `html` (cleaned), `both` or `none` (fastest). |
| `maxJobsPerCompany` | Default 1,000, newest first. |
| `maxItems` | Charged jobs in the whole run (default 1,000, up to 50,000). |
| `onlyNewJobs` | Monitor mode: only jobs not delivered before under `monitorName`, plus free `closed` rows. |
| `monitorName` | Separate watchlists keep separate histories (default `default`). |
| `skipExistingOnFirstRun` | Monitor mode: the first run saves today's jobs without returning or charging them, so you get alerts on new roles only. |
| `reportClosedJobs` | Monitor mode: free rows for delivered jobs that closed (default on). |
| `alertWebhookUrl` | Monitor mode: one message per run with new or closed jobs. Slack and Discord webhooks get readable text, other https URLs (Make, Zapier, n8n) get JSON. Stored as a secret. |
| `aiEnrichment` | Adds the AI fields to each delivered job, charged as its own event. |
| `maxConcurrency`, `requestTimeoutSecs`, `proxyConfiguration` | Advanced. No proxy is needed; residential proxy is not supported. |

Example: a daily monitor of three companies' engineering jobs in Europe, with a Slack alert.

```json
{
    "companies": ["https://jobs.ashbyhq.com/ramp", "stripe.com", "https://apply.workable.com/huggingface"],
    "titleKeywords": ["engineer", "developer"],
    "locations": ["Germany", "France", "Netherlands", "Remote"],
    "onlyNewJobs": true,
    "monitorName": "eu-engineering",
    "skipExistingOnFirstRun": true,
    "alertWebhookUrl": "https://hooks.slack.com/services/…"
}
```

### Output

One row per job. A Greenhouse job (description shortened):

```json
{
    "rowType": "job",
    "jobKey": "greenhouse:stripe:7012345",
    "input": "stripe.com",
    "ats": "greenhouse",
    "boardToken": "stripe",
    "boardUrl": "https://boards.greenhouse.io/stripe",
    "jobId": "7012345",
    "requisitionId": null,
    "company": "Stripe",
    "companyDomain": "stripe.com",
    "title": "Backend Engineer, Payments",
    "url": "https://stripe.com/jobs/search?gh_jid=7012345",
    "applyUrl": "https://stripe.com/jobs/search?gh_jid=7012345",
    "department": "Payments Engineering",
    "team": null,
    "location": "San Francisco, CA",
    "allLocations": ["San Francisco, CA"],
    "city": "San Francisco",
    "region": "California",
    "countryCode": "US",
    "workplaceType": null,
    "employmentType": null,
    "seniority": null,
    "postedAt": "2026-10-20T14:03:11.000Z",
    "updatedAt": "2026-10-22T09:00:00.000Z",
    "language": "en",
    "descriptionText": "Who we are\n\nAbout Stripe…",
    "descriptionHtml": null,
    "salaryMin": 180000,
    "salaryMax": 250000,
    "salaryCurrency": "USD",
    "salaryPeriod": "year",
    "salaryText": "$180,000 - $250,000",
    "salarySource": "description",
    "changeType": null,
    "firstSeenAt": null,
    "billable": true,
    "aiEnriched": false,
    "error": null,
    "scrapedAt": "2026-10-24T01:00:07.000Z",
    "aiSeniority": null,
    "aiExperienceYearsMin": null,
    "aiSkills": null,
    "aiJobFunction": null,
    "aiVisaSponsorship": null,
    "aiRemoteRegions": null,
    "aiSummary": null,
    "aiError": null
}
```

- `jobKey` is the same for the same job in every run: use it to deduplicate and to join runs.
- `salarySource` says where the numbers come from: `ats` (the board's own fields), `description` (a pay range in the text, as US pay-transparency laws require) or `ai`.
- Unknown values are `null`, never guesses: a Workday "Posted 30+ Days Ago" is not turned into a date, and `workplaceType: null` means not stated, not on-site.
- In monitor mode `changeType` is `new` or `closed`, and `firstSeenAt` is when the monitor first delivered the job.
- With `aiEnrichment`, the `ai*` fields are filled by Claude Haiku 5.5 from the title, locations and description. They are estimates; `aiError` says why a job could not be enriched (then it is not charged for AI).
- A company that cannot be read gets one free row with `rowType: "error"` and the reason.
- Views: **Jobs**, **AI** and **Errors**. The run's key-value store has a `SUMMARY` record with per-company counts (open, matched, new, closed jobs), jobs left out by each filter and AI usage.

### Pricing

Pay per event:

| Event | Free plan | Starter | Scale | Business |
|---|---|---|---|---|
| Job found (rows with `billable: true`) | $0.003 | $0.003 | $0.0024 | $0.0018 |
| Job enriched with AI (rows with `aiEnriched: true`, only when `aiEnrichment` is on) | $0.003 | $0.003 | $0.0024 | $0.0018 |
| Actor start (Apify's standard start fee) | $0.00005 | $0.00005 | $0.00005 | $0.00005 |

For example, 500 jobs from 10 companies cost $1.50 on the Free plan, or $3.00 with AI fields. A monitor run that finds nothing new costs only the start fee.

Free: jobs left out by your filters, closed-job rows, error rows, the first-run baseline of a monitor, and AI enrichment that failed. Set a maximum cost per run in the run options; the Actor stops cleanly when it is reached, and a job whose AI charge no longer fits is saved without the AI fields.

### Limits

- Only public job boards of these 8 systems. Other systems (iCIMS, SuccessFactors, Taleo, BambooHR, Teamtailor…) are not read yet; a company website that only links one of them gets an error row.
- A bare name can match a different company with the same board name on a small ATS; check `company` and `boardUrl`, or give the board link.
- Lever and Ashby publish no company name, so `company` is null there (use `input` or `companyDomain`).
- Workday has a weekly maintenance window (often Friday night to Saturday, US time) during which its boards answer with a maintenance page; the Actor then returns an error row for that company. Run again later.
- Some Personio boards publish no description in their feed; `descriptionText` is then null and AI enrichment is skipped for those jobs.
- With `maxJobsPerCompany` below a board's size, closed jobs are not reported for that company.

# Actor input Schema

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

One company per line. Best is its job board link: boards.greenhouse.io/stripe, jobs.lever.co/palantir, jobs.ashbyhq.com/ramp, nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite, careers.smartrecruiters.com/BoschGroup, apply.workable.com/huggingface, bunq.recruitee.com or ottonova.jobs.personio.de. Also accepted: platform and board name as greenhouse:stripe; a company website such as stripe.com, whose careers page must link to one of these boards; or a bare board name such as stripe, which is tried on Greenhouse, Lever, Ashby, Workable, Recruitee and Personio (Workday and SmartRecruiters need the link). Each company is read once; a company that cannot be read gets one free row with the reason.

## `titleKeywords` (type: `array`):

Keep only jobs whose title contains any of these words or phrases, ignoring case, whole words only ("engineer" matches "Software Engineer", not "Engineering Manager"). Jobs left out are not charged. Empty keeps every title.

## `excludeTitleKeywords` (type: `array`):

Leave out jobs whose title contains any of these words or phrases, ignoring case, whole words only, e.g. senior, intern. Wins over Title keywords.

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

Keep only jobs in one of these places: a city, US state, country name or two-letter country code (DE). Matched against every location of the job, whole words. Jobs with no location are left out when this is set.

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

Keep only jobs with one of these workplace types, as the board states it or as the location says (e.g. "Remote - US"). Jobs whose workplace type is unknown are left out when this is set; the run summary counts them.

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

Keep only jobs with one of these employment types. Jobs whose type is unknown are left out when this is set (Greenhouse rarely states one).

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

Keep only jobs whose department or team contains one of these texts, ignoring case, e.g. Engineering, Sales.

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

Keep only jobs posted in the last N days. Jobs whose board gives no posting date are left out when this is set.

## `onlyWithSalary` (type: `boolean`):

Keep only jobs with a salary from the board or from a pay range in the description text. A salary the AI step finds does not count here, so no AI charge is made for a job that is then left out.

## `descriptionFormat` (type: `string`):

How the job description is returned. html is the board's own markup with scripts, styles and frames removed. none is fastest: Workday and SmartRecruiters then skip one request per job. Descriptions are cut at 50,000 characters. AI enrichment needs a description and fetches it even with none.

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

At most this many jobs per company, newest first when the board gives dates. With a cap below the board's size, closed jobs are not reported for that company (the Actor cannot tell closed from not read).

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

At most this many charged job rows in the whole run. Free rows (closed jobs, errors) do not count. The run stops cleanly when it is reached.

## `onlyNewJobs` (type: `boolean`):

Monitor mode. Return only jobs not delivered before under this monitor name, plus free rows for delivered jobs that have closed. Schedule the Actor to get a feed. The first run returns every matching job, unless "Skip existing jobs on the first run" is on.

## `monitorName` (type: `string`):

Only with monitor mode. Runs with the same name share what was delivered; use different names for separate watchlists. Changing filters keeps the history: a job that now passes the filters and was never delivered comes as new.

## `skipExistingOnFirstRun` (type: `boolean`):

Only with monitor mode. On the first run of a monitor name for a company, save the jobs that are open now without returning or charging them; later runs return only jobs posted after that. Good for alerts on new roles only.

## `reportClosedJobs` (type: `boolean`):

Only with monitor mode. Add a free row with changeType "closed" for each delivered job that is no longer on its board, so a job board can delist it.

## `alertWebhookUrl` (type: `string`):

Optional. After a monitor run with new or closed jobs, send one short message: a Slack or Discord incoming webhook gets readable text, any other https URL (Make, Zapier, n8n) gets JSON. Nothing is sent when nothing changed.

## `aiEnrichment` (type: `boolean`):

Add AI fields to each delivered job: salary from the description text when the board gives none, seniority, years of experience, skills, job function, visa sponsorship, remote regions and a two-sentence summary. Charged per enriched job as a separate event; a job the AI step could not enrich is not charged for it.

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

Companies read at the same time. Requests to one ATS host are also limited per host.

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

Time limit per HTTP request. A company whose board does not answer gets a free row with the error.

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

Not needed: these are public APIs that companies publish so their jobs get indexed. Apify datacenter proxy or your own proxies work if a board rate-limits you. Residential proxy is not supported.

## Actor input object example

```json
{
  "companies": [
    "https://boards.greenhouse.io/stripe",
    "https://jobs.lever.co/palantir",
    "https://jobs.ashbyhq.com/ramp",
    "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite"
  ],
  "onlyWithSalary": false,
  "descriptionFormat": "text",
  "maxJobsPerCompany": 10,
  "maxItems": 1000,
  "onlyNewJobs": false,
  "monitorName": "default",
  "skipExistingOnFirstRun": false,
  "reportClosedJobs": true,
  "aiEnrichment": false,
  "maxConcurrency": 5,
  "requestTimeoutSecs": 30,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

One row per job: title, company, location, workplace, posting date, salary, link, monitor change and whether the row was charged.

## `ai` (type: `string`):

Job function, level, skills, remote regions, visa sponsorship and summary per job. Filled only when AI enrichment is on.

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

Companies read and failed, open, matched and charged jobs per company, jobs left out by each filter, AI usage and why the run stopped early, if it did.

# 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": [
        "https://boards.greenhouse.io/stripe",
        "https://jobs.lever.co/palantir",
        "https://jobs.ashbyhq.com/ramp",
        "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite"
    ],
    "descriptionFormat": "text",
    "maxJobsPerCompany": 10,
    "aiEnrichment": false
};

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

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

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

```

## Python example

```python
from apify_client import ApifyClient

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

# Prepare the Actor input
run_input = {
    "companies": [
        "https://boards.greenhouse.io/stripe",
        "https://jobs.lever.co/palantir",
        "https://jobs.ashbyhq.com/ramp",
        "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite",
    ],
    "descriptionFormat": "text",
    "maxJobsPerCompany": 10,
    "aiEnrichment": False,
}

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

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

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

```

## CLI example

```bash
echo '{
  "companies": [
    "https://boards.greenhouse.io/stripe",
    "https://jobs.lever.co/palantir",
    "https://jobs.ashbyhq.com/ramp",
    "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite"
  ],
  "descriptionFormat": "text",
  "maxJobsPerCompany": 10,
  "aiEnrichment": false
}' |
apify call fetchwise/career-site-jobs-scraper --silent --output-dataset

```

## MCP server setup

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

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

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/1nzq7RTwiSc1AAT9o/builds/Kbbjgnfl7popagszG/openapi.json
