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

Scrape job postings straight from company career sites on Greenhouse, Lever, Ashby, Workday, SmartRecruiters, Recruitee, Workable and Personio. One clean format with salaries, locations, remote flag and full descriptions. Monitor new jobs on a schedule.

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

## Pricing

from $2.21 / 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/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 & more

Scrape job postings **straight from company career sites** – the same public job-board feeds that power "Careers" pages – and get one clean, normalized dataset across eight applicant tracking systems (ATS). Paste career-page URLs, pick your filters, and get titles, locations, remote flags, salaries, dates, apply links and full descriptions in seconds.

**Built by [Artificially](https://apify.com/artificially)** | Follow us on [X @artificially\_io](https://x.com/artificially_io)

#### Use with AI agents (MCP)

This actor works as a tool for Claude, ChatGPT, Cursor and other AI agents through [Apify's MCP server](https://mcp.apify.com). Ask something like "list open engineering jobs at Stripe and Ramp" and the agent calls it for you. Set `compactOutput: true` and a small `maxJobsPerCompany` or `maxResults` (e.g. 10) so results fit easily in the agent's context.

**Quick setup** (sign in with your Apify account when asked):

- **Claude** (claude.ai or Claude Desktop): Settings → Connectors → Add custom connector, and paste `https://mcp.apify.com?tools=artificially/career-site-jobs-scraper`
- **Claude Code or Cursor** via the Apify CLI (latest version, `apify upgrade`): `apify mcp install claude-code --tools artificially/career-site-jobs-scraper` (use `cursor` instead of `claude-code` for Cursor)
- **Any MCP client** (Cursor, VS Code, Windsurf):

```json
{
  "mcpServers": {
    "apify": { "url": "https://mcp.apify.com?tools=artificially/career-site-jobs-scraper" }
  }
}
```

**Try asking:**

- "Get all open engineering jobs at Airbnb, Stripe and Notion from their career sites, with salaries."
- "Monitor these 10 company career pages and tell me which remote jobs were posted this week."

### Why this actor

- **Straight from the source** – jobs come from the company's own ATS, not from a job board that re-posts them late or loses them. Every job has a direct apply link.
- **One schema for 8 ATS platforms** – Greenhouse, Lever, Ashby, Workday, SmartRecruiters, Recruitee, Workable and Personio all come out in the same shape.
- **Salary data where it exists** – structured pay ranges from the ATS (Ashby, Lever, Recruitee), plus pay ranges pulled out of the description text (US pay-transparency ranges like `$150,000 - $210,000/yr` or `148,000 USD - 235,750 USD`) with `salary.source` telling you which.
- **Real filters** – keywords, exclude keywords, locations, remote only, departments, employment types, posted within N days, limits per company and in total.
- **Monitoring mode** – turn on `onlyNewSinceLastRun`, schedule the actor, and each run returns only the jobs you have not seen yet.
- **Paste anything** – job-board URLs, `platform:slug` shorthand, a company's own careers page (the embedded ATS is detected automatically), or just a slug.
- **Fast and cheap to run** – public JSON feeds, no browser, no proxy needed. Hundreds of jobs per minute.
- **You only pay for results** – only valid jobs (with ID, title and URL) are saved and charged. Companies that fail are listed for free in `FAILED_COMPANIES`.

### Supported ATS platforms

| Platform | Example input | Shorthand | Salary | Notes |
|----------|---------------|-----------|--------|-------|
| Greenhouse | `https://job-boards.greenhouse.io/stripe` or `https://boards.greenhouse.io/airbnb` | `greenhouse:stripe` | From description | Departments, workplace type from board metadata |
| Lever | `https://jobs.lever.co/palantir` | `lever:palantir` | ATS range + description | Team, department, commitment, workplace type (EU boards supported) |
| Ashby | `https://jobs.ashbyhq.com/ramp` | `ashby:ramp` | Structured compensation | Best salary coverage, structured addresses, remote flag |
| Workday | `https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite` | `workday:nvidia.wd5/NVIDIAExternalCareerSite` | From description | Full pagination; keywords use Workday's own search |
| SmartRecruiters | `https://careers.smartrecruiters.com/ServiceNow` | `smartrecruiters:ServiceNow` | From description | Department, function, remote/hybrid flags |
| Recruitee | `https://bunq.recruitee.com` | `recruitee:bunq` | Structured (when published) | Multiple locations, remote/hybrid/on-site |
| Workable | `https://apply.workable.com/huggingface` | `workable:huggingface` | – | Remote flag, employment type |
| Personio | `https://1komma5grad.jobs.personio.de` | `personio:1komma5grad` | – | Offices, department, schedule |

Also accepted: a company's careers page such as `https://www.notion.com/careers` (the actor finds the embedded Greenhouse/Lever/Ashby/… board), or a bare slug such as `stripe` (probed on Greenhouse, Lever, Ashby, SmartRecruiters, Workable and Recruitee).

### Use cases

- **Job aggregators and job boards** – fill your board with fresh, first-party postings from hundreds of companies, deduplicated and normalized.
- **Recruiters and talent teams** – track openings at target companies, see which teams are hiring, and spot roles before they hit LinkedIn.
- **Sales "hiring signal" leads** – a company hiring 5 data engineers or its first RevOps manager is a buying signal. Feed new postings into your CRM daily.
- **Market and salary research** – compare pay ranges, remote policies and hiring velocity across companies, departments and countries.
- **Job seekers and communities** – build alerts for "remote + engineer + posted in the last 3 days" across your dream companies.

### Input

| Field | Type | Description | Default |
|-------|------|-------------|---------|
| `companies` | array | Career-site URLs, `platform:slug`, careers pages or slugs | Required |
| `keywords` | array | Keep jobs matching ANY keyword (title, and description if enabled) | – |
| `keywordsInDescription` | boolean | Also match keywords in the description | `true` |
| `excludeKeywords` | array | Drop jobs whose title contains ANY of these | – |
| `locations` | array | Keep jobs whose location contains ANY of these (city, region, country, "remote") | – |
| `remoteOnly` | boolean | Only remote jobs | `false` |
| `departments` | array | Keep jobs whose department/team contains ANY of these | – |
| `employmentTypes` | array | `full-time`, `part-time`, `contract`, `temporary`, `internship`, `other` | – |
| `postedWithinDays` | integer | Only jobs published in the last N days | – |
| `maxJobsPerCompany` | integer | Cap per company | – |
| `maxResults` | integer | Cap for the whole run | – |
| `includeDescription` | boolean | Return `descriptionHtml` and `descriptionText` | `true` |
| `onlyNewSinceLastRun` | boolean | Return only jobs not returned by earlier runs with the same input | `false` |
| `compactOutput` | boolean | Slim items with only the key fields (ID, platform, company, title, department, location, remote, workplace, type, salary, date, URL). Best for AI agents | `false` |
| `maxConcurrency` | integer | Companies fetched in parallel (1–10) | `5` |
| `proxyConfiguration` | object | Optional proxy; not needed for public ATS feeds | none |

#### Example input

```json
{
    "companies": [
        "https://job-boards.greenhouse.io/stripe",
        "lever:palantir",
        "https://jobs.ashbyhq.com/ramp",
        "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite",
        "https://www.notion.com/careers"
    ],
    "keywords": ["engineer", "developer"],
    "keywordsInDescription": false,
    "remoteOnly": true,
    "postedWithinDays": 14,
    "maxJobsPerCompany": 100
}
```

### Output

Every job has the same structure, whatever ATS it came from:

```json
{
    "jobId": "ashby:notion:1fc309c8-da20-4ff2-84c7-8b863ece2b0a",
    "platform": "ashby",
    "company": {
        "name": "Notion",
        "slug": "notion",
        "careersUrl": "https://jobs.ashbyhq.com/notion"
    },
    "title": "Software Engineer, Developer Platform",
    "department": "Engineering",
    "team": "Engineering",
    "location": "San Francisco, California; New York, New York",
    "locations": [
        { "city": "San Francisco", "region": "California", "country": "United States" },
        { "city": "New York", "region": "New York", "country": "United States" }
    ],
    "isRemote": true,
    "workplaceType": "hybrid",
    "employmentType": "full-time",
    "employmentTypeRaw": "FullTime",
    "salary": {
        "min": 213000,
        "max": 320000,
        "currency": "USD",
        "interval": "year",
        "raw": "$213,000 - $320,000 per year",
        "source": "description"
    },
    "postedAt": "2026-08-24T14:44:49.699Z",
    "updatedAt": null,
    "applyUrl": "https://jobs.ashbyhq.com/notion/1fc309c8-da20-4ff2-84c7-8b863ece2b0a/application",
    "jobUrl": "https://jobs.ashbyhq.com/notion/1fc309c8-da20-4ff2-84c7-8b863ece2b0a",
    "descriptionText": "WHO WE ARE\n\nNotion is the collaborative AI workspace where teams and agents think together...",
    "descriptionHtml": "<p><strong>WHO WE ARE</strong></p>...",
    "scrapedAt": "2026-09-23T09:06:07.386Z"
}
```

| Field | Description |
|-------|-------------|
| `jobId` | Stable unique ID `platform:company:id` – use it to deduplicate across runs |
| `platform` | ATS the job came from |
| `company` | `name`, `slug` and `careersUrl` |
| `title`, `department`, `team` | As published by the company |
| `location` | Raw location text from the ATS |
| `locations` | Parsed list of `{ city, region, country }` |
| `isRemote` | `true` if the ATS marks the job remote or the location says remote |
| `workplaceType` | `remote`, `hybrid`, `onsite` or `null` when unknown |
| `employmentType` | Normalized: `full-time`, `part-time`, `contract`, `temporary`, `internship`, `other` (`employmentTypeRaw` keeps the original) |
| `salary` | `{ min, max, currency, interval, raw, source }` or `null`; `source` is `ats` (structured) or `description` (parsed from the text) |
| `postedAt`, `updatedAt` | ISO 8601 dates |
| `applyUrl`, `jobUrl` | Direct links to the posting and the application form |
| `descriptionText`, `descriptionHtml` | Full description (when `includeDescription` is on) |
| `scrapedAt` | When the job was collected |

The key-value store also contains:

- `SUMMARY` – per-company and per-platform counts, limits reached.
- `FAILED_COMPANIES` – inputs that could not be resolved or fetched, with a reason (`not_found`, `invalid_input`, `detection_failed`, `no_open_jobs`, `http_error`). These are never charged.

### Pricing

This actor uses pay-per-event pricing: a small fee per run plus a fee per job saved to the dataset. **You only pay for results** – invalid jobs are never saved, duplicates are removed, and failed companies cost nothing. Set a maximum cost per run in Apify Console and the actor stops cleanly when it is reached. See the **Pricing** tab for current prices.

### FAQ

**How do I monitor companies for new jobs?**
Turn on `onlyNewSinceLastRun` and create an [Apify Schedule](https://docs.apify.com/platform/schedules) (for example every morning). The actor remembers every job ID it has returned for that exact input (same companies and filters) and the next runs return only new postings, so you pay only for jobs you have not seen. Connect the run to Slack, email, Google Sheets, Make, Zapier or a webhook to get alerts. Changing the companies or filters starts a fresh history.

**How do I find the right URL for a company?**
Open the company's careers page and click any job. The address bar usually shows the ATS: `job-boards.greenhouse.io/…`, `jobs.lever.co/…`, `jobs.ashbyhq.com/…`, `….myworkdayjobs.com/…`, and so on. You can also paste the careers page itself and let the actor detect it.

**Why did a company fail?**
Check `FAILED_COMPANIES`. The most common reasons are a wrong slug (`not_found`), a board with no open roles (`no_open_jobs`), or a careers page that uses an ATS we do not support yet (`detection_failed`). Failed companies are not charged.

**Why is salary missing for some jobs?**
Many companies do not publish pay. Where the ATS has structured pay (Ashby, Lever, Recruitee) you get it directly; otherwise we extract ranges from the description text. We never guess.

**How does the employment type filter treat jobs without a type?**
Some ATS (for example Greenhouse) rarely publish an employment type. Titles like "Intern" or "Contractor" are recognized automatically; other jobs without a type are kept only when you select `full-time`.

**Is this legal?**
The actor reads the public job-board feeds that companies publish so that their jobs can be embedded and shared. It does not log in, bypass protection, or collect personal data about candidates. You are responsible for using the data in line with applicable laws and the sites' terms.

**Do I need a proxy?**
No. The feeds are public APIs. A proxy input is available if your network requires one.

**Large Workday companies are slow – why?**
Workday lists jobs 20 at a time and needs one extra request per job for the description. Turn off `includeDescription` (and keyword matching in descriptions) to skip the detail requests, or use `maxJobsPerCompany`.

### Support

Found a company that does not work, or want another ATS supported? Open an issue on the actor's **Issues** tab – we usually respond within a day.

### Related actors

- [LinkedIn Jobs Scraper](https://apify.com/artificially/linkedin-jobs-scraper): job postings from LinkedIn search, across all companies.
- [LinkedIn Company Scraper](https://apify.com/artificially/linkedin-company-scraper): company profiles with size, industry, headquarters and more.
- [LinkedIn Company Employees Scraper](https://apify.com/artificially/linkedin-employees-scraper): people working at a company, to find the hiring manager or recruiter.

# Actor input Schema

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

Required. One company per entry. Accepted formats: a job-board URL (e.g. https://job-boards.greenhouse.io/stripe, https://jobs.lever.co/palantir, https://jobs.ashbyhq.com/ramp, https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite, https://careers.smartrecruiters.com/ServiceNow, https://bunq.recruitee.com, https://apply.workable.com/huggingface, https://1komma5grad.jobs.personio.de); the shorthand platform:slug (e.g. greenhouse:stripe, lever:palantir, ashby:ramp, workday:nvidia.wd5/NVIDIAExternalCareerSite); a company's own careers page (e.g. https://www.notion.com/careers, the embedded ATS is detected automatically); or just a slug (e.g. stripe) to probe the common platforms. Example: \["greenhouse:stripe", "https://jobs.lever.co/palantir"].

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

Keep jobs whose title (or description, see keywordsInDescription) contains ANY of these words or phrases. Case-insensitive. Example: \["engineer", "data scientist"]. Leave empty to keep all jobs.

## `keywordsInDescription` (type: `boolean`):

If true, a keyword match in the job description also counts. Set to false to match job titles only (stricter, fewer false positives).

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

Drop jobs whose title contains ANY of these words. Case-insensitive. Example: \["senior", "intern", "manager"].

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

Keep jobs whose location contains ANY of these strings (city, state/region, country, or "remote"). Case-insensitive substring match. Example: \["London", "United States", "Germany"].

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

Set to true to keep only jobs marked remote by the ATS or with "remote" in the location.

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

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

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

Keep only these employment types. Allowed values: full-time, part-time, contract, temporary, internship, other. Example: \["full-time", "contract"]. Jobs whose ATS does not publish a type are kept only when full-time is selected. Leave empty for all types.

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

Keep only jobs published in the last N days, as an integer from 1 to 3650. Example: 7. Leave empty for any date.

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

Stop after saving this many jobs from a single company. Example: 10. Leave empty for no limit.

## `maxResults` (type: `integer`):

Stop the run after saving this many jobs in total across all companies. Example: 50. Leave empty for no limit (the run also stops at your maximum cost per run).

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

Return the full description as both HTML (descriptionHtml) and clean plain text (descriptionText). Set to false for smaller, faster output (Workday and SmartRecruiters then skip per-job detail requests). Descriptions are never returned when compactOutput is on.

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

Set to true to remember job IDs already returned for this exact input (companies + filters) and return only jobs you have not seen before. Ideal with Apify Schedules: you pay only for new postings.

## `compactOutput` (type: `boolean`):

Set to true to save a slim item per job with only the key fields: jobId, platform, company.name, title, department, location, isRemote, workplaceType, employmentType, salary (min, max, currency, interval), postedAt and jobUrl. Descriptions, parsed locations, raw fields and URLs other than jobUrl are dropped. Recommended when an AI agent or LLM reads the results; combine with includeDescription false for the fastest runs. Leave false for the full item.

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

How many companies are fetched at the same time, from 1 to 10. Requests to each ATS host are additionally rate-limited to stay polite.

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

Optional, leave as is. The ATS endpoints used are public job-board APIs and normally work without a proxy.

## Actor input object example

```json
{
  "companies": [
    "https://job-boards.greenhouse.io/airbnb",
    "https://jobs.lever.co/spotify",
    "https://jobs.ashbyhq.com/notion"
  ],
  "keywordsInDescription": true,
  "remoteOnly": false,
  "maxJobsPerCompany": 10,
  "includeDescription": true,
  "onlyNewSinceLastRun": false,
  "compactOutput": false,
  "maxConcurrency": 5
}
```

# Actor output Schema

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

One item per job posting (overview table view)

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

Per-company counts, per-platform totals and limits reached

## `failedCompanies` (type: `string`):

Inputs that could not be resolved or fetched, with a reason (never charged)

# 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://job-boards.greenhouse.io/airbnb",
        "https://jobs.lever.co/spotify",
        "https://jobs.ashbyhq.com/notion"
    ],
    "maxJobsPerCompany": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("artificially/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://job-boards.greenhouse.io/airbnb",
        "https://jobs.lever.co/spotify",
        "https://jobs.ashbyhq.com/notion",
    ],
    "maxJobsPerCompany": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("artificially/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://job-boards.greenhouse.io/airbnb",
    "https://jobs.lever.co/spotify",
    "https://jobs.ashbyhq.com/notion"
  ],
  "maxJobsPerCompany": 10
}' |
apify call artificially/career-site-jobs-scraper --silent --output-dataset

```

## MCP server setup

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