# Career Site Jobs Scraper | Greenhouse, Lever, Workday | $0.9/1K (`hridayrungta/career-site-jobs-scraper`) Actor

Jobs API for company career sites: Greenhouse, Lever, Ashby, Workday, Workable, SmartRecruiters, Recruitee. Paste board URLs or names; get title, location, remote, salary, apply link, description. Filters plus only-new-jobs monitoring. $0.90 per 1,000 jobs.

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

## Pricing

from $0.63 / 1,000 job scrapeds

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

The **Career Site Jobs Scraper** is a **jobs API for company career sites**. Give it the careers pages of the companies you care about, on **Greenhouse, Lever, Ashby, Workday, Workable, SmartRecruiters or Recruitee**, and it returns every open job as one clean row: title, location, remote flag, department, employment type, posted date, **salary when the company publishes it**, apply link and the full description. The easiest way to try it: press **Start** with the prefilled boards (Discord on Greenhouse and Linear on Ashby) and you get 40 jobs in a few seconds.

The headline use case is **monitoring**: switch on **Only new jobs** and schedule the Actor, and every run returns only the jobs that were posted since the previous run. That turns any list of companies into a live feed of new openings for job alerts, sales signals, recruiting research or a job board.

- **Seven ATSs in one Actor**: Greenhouse jobs, Lever jobs, Ashby jobs, Workday jobs, Workable, SmartRecruiters and Recruitee. One input list, one output format.
- **Auto-detects the ATS** from any board URL, from a short form like `greenhouse:stripe`, from a bare company name (we try each ATS for you), or from the company's own careers page (we find the job board link on it).
- **One normalized row per job** with the same fields for every ATS, so Greenhouse and Workday jobs land in the same spreadsheet columns.
- **Filters**: job title contains, title excludes, location contains, remote only, posted within N days, department or team.
- **Only new jobs since the last run**, kept in a key-value store on your own account.
- **Public endpoints only**: the same JSON each company's careers page loads in your browser. No login, no API key, no cookies, no proxy needed.
- **Apify platform advantages**: run it from the API, schedule it, connect it to Make, n8n, Zapier, Google Sheets or webhooks, monitor runs, and call it from AI agents through Apify's MCP server.

### What data can you extract from career sites?

| Field | What it is |
|---|---|
| `company` | Company name (from the board; for Lever and Ashby boards it is derived from the board name, e.g. `spotify` -> `Spotify`) |
| `ats` | `greenhouse`, `lever`, `ashby`, `workable`, `smartrecruiters`, `recruitee` or `workday` |
| `jobId` | The job's ID on that ATS (Workday: the requisition ID) |
| `title` | Job title |
| `location`, `locations`, `country` | Main location, every listed location, and the country when the board gives one |
| `isRemote`, `workplaceType` | Remote flag, and Remote / Hybrid / On-site when known |
| `department`, `team` | Department and team as the company labels them |
| `employmentType` | Full-time, Part-time, Contract, Internship, Temporary |
| `postedAt`, `updatedAt` | When the job was published (and last updated, where the ATS says) |
| `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryPeriod` | Structured pay range when the company publishes one (Greenhouse pay transparency, Ashby compensation, Lever, SmartRecruiters, Recruitee) |
| `salaryText` | The pay range as written, including ranges found in the description text (e.g. `$87,069-$124,385`) |
| `jobUrl`, `applyUrl` | The job page and the application link |
| `descriptionText`, `descriptionHtml` | Full job description as clean plain text and as the original HTML |
| `isNew` | `true` on rows returned in "Only new jobs" mode |
| `boardUrl`, `boardSlug` | The board the job came from |
| `status`, `input`, `error` | `ok` for a job; `failed` with the reason for a board that could not be read (free) |

### How to use the Career Site Jobs Scraper

1. Open the Actor and paste your companies into **Career boards or companies**, one per line: board URLs such as `https://boards.greenhouse.io/stripe`, `https://jobs.lever.co/spotify`, `https://jobs.ashbyhq.com/linear`, `https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite`, short forms such as `lever:palantir`, or plain names such as `Hugging Face`.
2. Optionally add filters: title keywords (`engineer`, `sales`), locations (`London`, `Remote`), remote only, or posted within the last 7 days.
3. For monitoring, switch on **Only new jobs since the last run**.
4. Press **Start**. Jobs appear in the **Jobs** table as they are found.
5. Download the results as CSV, Excel or JSON, or get them through the API.
6. To monitor, create a **Schedule** (for example daily at 08:00) and connect a webhook, Google Sheets, Slack or email integration so new jobs reach you automatically.

### Input

| Field | Type | What it does |
|---|---|---|
| `boards` | list | Board URLs, `ats:slug` lines, company names or careers page URLs. Required |
| `atsForNames` | select | ATS to use for bare company names. Default `auto` tries Greenhouse, Lever, Ashby, Workable, SmartRecruiters, Recruitee |
| `titleKeywords` | list | Keep jobs whose title contains any of these words |
| `excludeTitleKeywords` | list | Drop jobs whose title contains any of these words |
| `locations` | list | Keep jobs whose location or country contains any of these |
| `remoteOnly` | boolean | Remote jobs only |
| `postedWithinDays` | integer | Only jobs posted in the last N days (0 = any) |
| `departments` | list | Keep jobs whose department or team contains any of these |
| `onlyNewJobs` | boolean | Return only jobs not returned by a previous run |
| `firstRunMode` | select | First monitoring run: return all current jobs, or just remember them (baseline) |
| `stateStoreName`, `stateNamespace` | text | Where the seen job IDs are kept, to run several independent monitors |
| `includeDescription` | boolean | Full description as text and HTML (default on) |
| `maxJobsPerBoard` | integer | Cap per board (default 1000, 0 = no limit) |
| `maxItems` | integer | Cap for the whole run (0 = no limit) |

Input example (engineering jobs in New York or remote, posted in the last 30 days, from three companies on three different ATSs):

```json
{
  "boards": [
    "greenhouse:datadog",
    "https://jobs.lever.co/palantir",
    "https://pfizer.wd1.myworkdayjobs.com/PfizerCareers"
  ],
  "titleKeywords": ["engineer", "scientist"],
  "locations": ["New York", "Remote"],
  "postedWithinDays": 30,
  "maxJobsPerBoard": 30
}
```

### Output example

A real row from the prefilled run (descriptions shortened here):

```json
{
  "status": "ok",
  "ats": "greenhouse",
  "company": "Discord",
  "boardSlug": "discord",
  "boardUrl": "https://job-boards.greenhouse.io/discord",
  "jobId": "8840756002",
  "title": "Data Scientist - Client Platform",
  "location": "San Francisco Bay Area",
  "locations": ["San Francisco Bay Area", "San Francisco, California, United States"],
  "country": null,
  "isRemote": false,
  "workplaceType": null,
  "department": "Data Science & Engineering",
  "team": null,
  "employmentType": null,
  "postedAt": "2026-09-24T21:26:49.000Z",
  "updatedAt": "2026-10-21T00:14:41.000Z",
  "salaryMin": 160000,
  "salaryMax": 200000,
  "salaryCurrency": "USD",
  "salaryPeriod": "year",
  "salaryText": "$160,000-$200,000 USD",
  "jobUrl": "https://job-boards.greenhouse.io/discord/jobs/8840756002",
  "applyUrl": "https://job-boards.greenhouse.io/discord/jobs/8840756002",
  "descriptionText": "Discord has a highly engaged community of millions of daily active users who use the platform for many different reasons, but there’s one thing that nearly ever ...",
  "descriptionHtml": "<div class=\"content-intro\"><p>Discord has a highly engaged community of millions of daily active users who use the platf ...",
  "isNew": null,
  "scrapedAt": "2026-11-02T10:59:00.079Z"
}
```

### Monitor new job postings with "Only new jobs"

Switch on `onlyNewJobs` and schedule the Actor. For every board it remembers the IDs of the jobs it already gave you, in a named key-value store on **your** Apify account (nobody else can read it). The next run returns only jobs that were not there before, each with `isNew: true`, and you pay only for those rows plus the small start fee.

- **First run**: by default it returns all current jobs and remembers them. Choose **Baseline only** if you only want jobs posted from now on: the first run then just remembers today's openings and returns nothing.
- **Nothing is lost**: a job is remembered only once it was delivered. If `maxJobsPerBoard` or `maxItems` cut a run short, the jobs you did not get are returned as new on the next run.
- **Closed jobs** drop out of the memory automatically when a board no longer lists them.
- **Several monitors**: use a different `stateStoreName` or `stateNamespace` per monitor (for example one for sales roles and one for engineering).

### How much does it cost to scrape career sites?

Pay per result, no monthly fee. Prices per tier of your Apify plan:

| Event | Free | Bronze | Silver | Gold, Platinum, Diamond |
|---|---|---|---|---|
| Job scraped (per job row) | $0.0009 | $0.00081 | $0.00072 | $0.00063 |
| Actor start (once per run) | $0.001 | $0.001 | $0.001 | $0.001 |

That is **$0.90 per 1,000 jobs** on the Free tier and $0.63 per 1,000 on Gold. Boards that fail, jobs removed by your filters and already-seen jobs in monitoring mode are not charged.

Worked examples (Free tier):

- **All open jobs at 10 companies** with about 1,500 jobs in total: 1,500 x $0.0009 + $0.001 = **about $1.35**.
- **Daily monitor of 50 companies** with about 20 new jobs a day: 30 runs x $0.001 + 600 x $0.0009 = **about $0.57 a month**.
- **Remote engineering jobs posted this week** at 100 companies, about 300 matches: **about $0.27**.

The Apify Free plan includes $5 of monthly credit, which covers about **5,500 job rows** a month at no cost.

### Integrations and scheduling

- **Apify Schedules**: run daily or hourly; combined with **Only new jobs** each run delivers just the fresh postings.
- **Make, n8n, Zapier**: use the Apify app to start a run and loop over the new jobs (post to Slack, add to a CRM, send an email digest).
- **Google Sheets**: export the dataset to a sheet, or append new jobs on each scheduled run.
- **Webhooks**: get a POST request when a run finishes and pull the dataset.
- **API**: start runs and read results with the Apify API or the JavaScript and Python clients. The dataset is plain JSON, CSV or Excel.
- **MCP for AI agents**: AI assistants such as Claude or ChatGPT can call the Actor through Apify's MCP server to answer "which companies on my list posted new data jobs this week?".

### FAQ

#### Why not use the Greenhouse API, Lever API or Workday API directly?

You can, but each ATS has its own URL format, paging, field names and quirks: Greenhouse escapes its HTML and hides pay ranges behind a flag, Lever pages 100 at a time, SmartRecruiters and Workday need one extra request per job for the description, and Workday reports the total only on the first page. This Actor handles all of that and gives you one format for all seven, plus filters, monitoring, retries, scheduling and integrations.

#### Is there a single jobs API for every company?

Not an official one. Most tech companies, startups and many large employers publish their jobs through one of these seven ATSs, and each has a public job-board feed. This Actor reads those feeds for the companies you name, which makes it a practical **job postings API** for your own company list.

#### How do I find a company's job board?

Open the company's careers page and click any job. The link usually shows the ATS: `boards.greenhouse.io/...`, `jobs.lever.co/...`, `jobs.ashbyhq.com/...`, `apply.workable.com/...`, `jobs.smartrecruiters.com/...`, `<company>.recruitee.com` or `<company>.wd5.myworkdayjobs.com/...`. You can also paste the careers page URL itself or just the company name and let the Actor detect it.

#### Can it search jobs across all companies, like a job search engine?

No. It reads the boards you give it. If you need a list of companies first, try our [Y Combinator Companies Scraper](https://apify.com/hridayrungta/ycombinator-companies-scraper), or search jobs by keyword on LinkedIn with our [LinkedIn Jobs Scraper](https://apify.com/hridayrungta/linkedin-jobs-scraper).

#### What are good alternatives?

For a keyword search over a large pre-collected database of career-site jobs, [fantastic-jobs Career Site Job Listing API](https://apify.com/fantastic-jobs/career-site-job-listing-api) is the best-known option. For a single ATS there are focused Actors such as the [Greenhouse Job Scraper](https://apify.com/bytepulselabs/greenhouse-job-scraper). This Actor is the choice when you have your own list of companies, want several ATSs in one format, and want only the new jobs on each run.

#### Is it legal to scrape job postings from career sites?

The Actor only reads job postings that companies publish openly for anyone to see, through the same public endpoints their careers pages use, with no login. Job ads are public business information, not personal data. You are responsible for how you use the data and for following each site's terms and the laws that apply to you.

#### Why did a board fail?

The status row tells you why. Common reasons: the board name is wrong or the company moved to another ATS, a SmartRecruiters company ID is case-sensitive (`BoschGroup`, not `boschgroup`), a Workday URL is missing the site part (`/NVIDIAExternalCareerSite`), or the careers page loads jobs from a site that is not one of the seven ATSs. Failed boards are free.

#### Why are some salary fields empty?

Many companies do not publish pay. Structured ranges come from the ATS when the company fills them in; `salaryText` also catches ranges written in the description. Workday and Workable do not have a structured salary field.

### Limits

- **Seven ATSs only**: Greenhouse, Lever, Ashby, Workable, SmartRecruiters, Recruitee and Workday. Custom careers sites, iCIMS, Taleo, SuccessFactors, BambooHR and others are not supported yet.
- **Workday** needs the full board URL (tenant, `wdN` and site name); it cannot be guessed from a company name. Workday lists show relative dates ("Posted 3 Days Ago"); the exact date is read from the job page when descriptions are on. Very large Workday sites may stop listing after a few thousand jobs; use title keywords to narrow them.
- **SmartRecruiters and Workday** need one request per job for the description, so they are slower than the others (about 10-15 jobs per second). Turn off `includeDescription` for a fast overview.
- **Company names** for Lever and Ashby boards come from the board name (`elevenlabs` -> `Elevenlabs`), because those feeds do not include the company name.
- A few companies keep a job in the Ashby feed while their hosted Ashby page is switched off, so the `jobUrl` can return 404 for those; the apply flow on the company's own site still works.
- Data is as current as the board at run time; it does not include jobs that were already closed.

### Related Actors

- [LinkedIn Jobs Scraper](https://apify.com/hridayrungta/linkedin-jobs-scraper): search LinkedIn jobs by keyword and location, with a new and closed jobs mode.
- [Y Combinator Companies Scraper](https://apify.com/hridayrungta/ycombinator-companies-scraper): build a company list (many YC startups hire through Ashby, Lever and Greenhouse).
- [Contact Info Scraper](https://apify.com/hridayrungta/contact-info-scraper): emails, phone numbers and social links from the websites of hiring companies.
- [Google Maps Email Extractor](https://apify.com/hridayrungta/google-maps-email-extractor): local businesses with their emails, for outreach.
- [Career Site Job Listing API by fantastic-jobs](https://apify.com/fantastic-jobs/career-site-job-listing-api): keyword search over a large database of career-site jobs.

# Actor input Schema

## `boards` (type: `array`):

One per line. Paste a career board URL (boards.greenhouse.io/stripe, jobs.lever.co/spotify, jobs.ashbyhq.com/linear, apply.workable.com/huggingface, jobs.smartrecruiters.com/Wise, bunq.recruitee.com, nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite), a short form such as greenhouse:stripe, a bare company name (we try each ATS for you), or the company's own careers page URL (we look for the job board link on it). The ATS is detected automatically from the URL.

## `atsForNames` (type: `string`):

Used only for lines that are a plain company name (no URL). Auto tries Greenhouse, Lever, Ashby, Workable, SmartRecruiters and Recruitee in that order and uses the first one with open jobs. Workday always needs the full URL.

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

Keep a job only if its title contains at least one of these words (case-insensitive), e.g. engineer, data, marketing. Leave empty for all jobs. On Workday this also narrows the search on Workday's side, which makes big boards much faster.

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

Drop jobs whose title contains any of these words, e.g. intern, senior, manager.

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

Keep a job only if its location, any of its locations or its country contains one of these, e.g. London, Germany, United States, Remote.

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

Keep only jobs marked remote by the board, or with 'remote' in the location or title.

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

Keep only jobs posted in the last N days (0 = any date). Workday list dates are approximate ('Posted 3 Days Ago'), the exact date is read from the job page.

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

Keep a job only if its department or team contains one of these, e.g. Engineering, Sales.

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

Remembers which jobs each board already returned (in a key-value store on your own account) and returns only jobs that are new since your previous run. Schedule the Actor daily or hourly to get a feed of new openings. Each row then has isNew = true.

## `firstRunMode` (type: `string`):

What the first run with 'Only new jobs' does for a board it has never seen. 'Return all current jobs' delivers today's openings (and remembers them). 'Baseline only' just remembers today's openings and returns nothing, so you are charged only for jobs posted after that.

## `stateStoreName` (type: `string`):

Name of the key-value store on your account that keeps the seen job IDs. Change it to run separate, independent monitors.

## `stateNamespace` (type: `string`):

Optional label to keep several independent monitors in the same store, e.g. 'sales-roles'. Leave empty to use your Apify user ID.

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

Adds the full description as plain text and HTML. On SmartRecruiters and Workday this needs one extra request per job, so turning it off makes those boards much faster.

## `maxJobsPerBoard` (type: `integer`):

Stop after this many matching jobs from one board (0 = no limit).

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

Stop the whole run after this many job rows (0 = no limit). Status rows for boards that failed are free and do not count.

## `maxRetries` (type: `integer`):

How many times a failed request is retried before the board or job is reported as failed.

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

Not needed: the job boards' public APIs answer without a proxy. Only set one if you see blocks.

## Actor input object example

```json
{
  "boards": [
    "https://boards.greenhouse.io/discord",
    "https://jobs.ashbyhq.com/linear"
  ],
  "atsForNames": "auto",
  "remoteOnly": false,
  "postedWithinDays": 0,
  "onlyNewJobs": false,
  "firstRunMode": "all",
  "stateStoreName": "career-site-jobs-state",
  "includeDescription": true,
  "maxJobsPerBoard": 20,
  "maxItems": 0,
  "maxRetries": 3,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

No description

## `allFields` (type: `string`):

No description

# 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 = {
    "boards": [
        "https://boards.greenhouse.io/discord",
        "https://jobs.ashbyhq.com/linear"
    ],
    "maxJobsPerBoard": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("hridayrungta/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 = {
    "boards": [
        "https://boards.greenhouse.io/discord",
        "https://jobs.ashbyhq.com/linear",
    ],
    "maxJobsPerBoard": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("hridayrungta/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 '{
  "boards": [
    "https://boards.greenhouse.io/discord",
    "https://jobs.ashbyhq.com/linear"
  ],
  "maxJobsPerBoard": 20
}' |
apify call hridayrungta/career-site-jobs-scraper --silent --output-dataset

```

## MCP server setup

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