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

Scrape jobs from company career sites via public ATS feeds: Greenhouse, Lever, Ashby, Workday, Workable, Recruitee, Personio, Rippling, Teamtailor, Pinpoint, BambooHR, Breezy. Paste domains or careers URLs; get one schema with salary, remote flag, descriptions, new-job alerts and hiring signals.

- **URL**: https://apify.com/signalscouter/career-site-jobs-scraper.md
- **Developed by:** [Signal Scout](https://apify.com/signalscouter) (community)
- **Categories:** Jobs, Lead generation
- **Stats:** 2 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

## Career Site Jobs Scraper: Greenhouse, Lever, Ashby, Workday, Teamtailor & more

**Get every open job straight from company career sites, in one clean dataset.** Paste a list of company domains like `stripe.com` or `notion.so`. The Actor finds which applicant tracking system (ATS) each company uses and pulls the jobs from that ATS's **official public job-board API**. That's the same feed the company's own careers page uses to show its job list.

✅ 12 ATS platforms, including UK/EU favourites Teamtailor, Pinpoint, BambooHR and Breezy · ✅ auto-detects the ATS from a domain · ✅ salary, remote flag and full descriptions · ✅ "new jobs only" monitoring · ✅ hiring-signal summary per company · ✅ no login, no browser, no proxies

*By **signalscouter**: reliable, low-cost data scrapers for hiring and market signals.*

***

### What does Career Site Jobs Scraper do?

Most job scrapers render careers pages in a headless browser and parse the HTML. That's slow, it breaks whenever a site is redesigned, and it often needs paid proxies. This Actor skips all of that. It calls the public JSON/XML endpoints that ATS platforms publish for each company's job board, then **normalizes every platform into one schema**.

The result:

- **Fast:** one request usually returns a company's entire job board.
- **Reliable:** a careers-page redesign doesn't break it.
- **Cheap to run:** no browser, no proxies.
- **Fresh:** jobs come from the source, often before they reach LinkedIn or Indeed.

### Supported ATS platforms

| ATS | Example input | Description | Structured salary |
|---|---|---|---|
| **Greenhouse** | `stripe.com`, `greenhouse:stripe`, `https://boards.greenhouse.io/stripe` | ✅ | parsed from text |
| **Lever** (US and EU) | `lever:palantir`, `https://jobs.lever.co/palantir` | ✅ | ✅ when published |
| **Ashby** | `notion.so`, `ashby:openai`, `https://jobs.ashbyhq.com/openai` | ✅ | ✅ when published |
| **Workday** | `https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite` | ✅ (one extra request per job) | parsed from text |
| **Workable** | `workable:huggingface`, `https://apply.workable.com/huggingface` | ✅ | parsed from text |
| **Recruitee** | `recruitee:bunq`, `https://bunq.recruitee.com` | ✅ | ✅ when published |
| **Personio** | `personio:personio`, `https://acme.jobs.personio.de` | ✅ | parsed from text |
| **Rippling** | `rippling:rippling`, `https://ats.rippling.com/rippling` | ✗ (titles, departments, locations) | ✗ |
| **Teamtailor** | `savills.com`, `teamtailor:savills`, `https://savills.teamtailor.com`, custom career sites like `https://careers.savills.com` | ✅ | parsed from text |
| **Pinpoint** | `davies-group.com`, `pinpoint:davies`, `https://menzies.pinpointhq.com` | ✅ | ✅ when published |
| **BambooHR** | `purplebricks.co.uk`, `bamboohr:savanta`, `https://savanta.bamboohr.com/careers` | ✅ (one extra request per job) | ✅ when published |
| **Breezy HR** | `endclothing.com`, `breezy:end`, `https://ajs.breezy.hr` | ✅ (one extra request per job) | ✅ when published |

"Parsed from text" means the Actor extracts pay ranges from pay-transparency sections in the description (e.g. "$135,000 – $200,000" or "£350 – £450 per day"). Pay strings are normalized into `salaryMin`, `salaryMax`, an ISO currency (GBP, EUR, USD and more) and a period (hour, day, week, month or year). The `salarySource` field tells you whether a salary came from the ATS (`ats`) or from the description text (`description`).

### What data do you get?

**Per job (`recordType: "job"`):**

- Company name, ATS, board slug, job ID and a stable `uid`
- Title, department, team
- Location, all locations, `isRemote`, workplace type, employment type
- `postedAt`, `updatedAt` where the ATS provides them
- Job URL and apply URL
- `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryInterval`, `salarySource`
- `descriptionText`, with `descriptionHtml` optional
- `isNew` in monitoring mode, `input`, `scrapedAt`

**Per company (`recordType: "companySummary"`, optional):**

- Open jobs; jobs posted in the last 7 and 30 days
- Remote jobs; jobs with salary
- `newSinceLastRun` in monitoring mode
- Top departments and top locations

### Use cases

- 🧭 **Job boards and aggregators:** power a niche board (AI, climate, remote, EU startups) with postings straight from employers.
- 📈 **Sales prospecting and hiring-signal leads:** a company opening 10 sales or data roles this month is a strong buying-intent signal. Feed the company summaries into your CRM or outbound tool.
- 🧑‍💼 **Recruiters and sourcers:** track which companies are hiring for your specialty, and get new roles the day they open.
- 🔬 **Market and labor research:** compare team growth, remote share and published pay ranges across companies.
- 🎯 **Job seekers:** watch a list of target companies and receive only new matching jobs every morning.

### How to use it

1. Click **Try for free** and add companies to **Companies**, one per line. Any of these formats work:
   - a plain domain: `stripe.com`
   - a careers or ATS URL: `https://jobs.lever.co/palantir`
   - an explicit board: `ashby:openai`
2. Optionally add filters: keywords, excluded keywords, locations, departments, remote only, posted within N days.
3. Click **Start**. Then download the results as JSON, CSV or Excel, or read them through the API.

#### Input example: plain domains with filters

```json
{
  "companies": ["stripe.com", "notion.so", "linear.app", "https://jobs.ashbyhq.com/openai", "lever:palantir"],
  "keywords": ["engineer", "data"],
  "excludeKeywords": ["intern"],
  "locations": ["remote", "london", "new york"],
  "includeCompanySummary": true
}
```

#### Input example: monitoring mode (new jobs only)

Turn on **Only new jobs since last run** and **schedule** the Actor (for example, daily at 08:00). The Actor remembers the job IDs it has already seen, in a named key-value store in your account. Each run then outputs **only jobs that weren't there last time**, ready for Slack, email or CRM alerts.

```json
{
  "companies": ["stripe.com", "notion.so", "workable:huggingface"],
  "keywords": ["sales", "account executive"],
  "onlyNewJobs": true
}
```

#### How ATS detection works

For a plain domain, the Actor:

1. Looks for ATS links on the homepage, `/careers`, `/jobs`, `careers.<domain>` and `jobs.<domain>`.
2. If it finds none, tries the obvious board name on Greenhouse, Ashby, Lever and Workable.

If a company still isn't detected, you get an `error` record (`ATS_NOT_DETECTED`). Pass its careers URL or `ats:slug` instead. Workday boards need the full `myworkdayjobs.com` URL.

### Output sample

| companyName | title | department | location | isRemote | salaryMin | salaryMax | ats |
|---|---|---|---|---|---|---|---|
| Openai | Research Engineer | Research | San Francisco | false | 250000 | 445000 | ashby |
| Notion | Engineering Manager, Mobile AI | Engineering | New York, New York | true | 255000 | 290000 | ashby |
| Palantir | Backend Software Engineer - Defense | — | Washington, D.C. | false | 135000 | 200000 | lever |
| Hugging Face | Open-Source Machine Learning Engineer - EMEA Remote | Open Source | Paris, Île-de-France, France | true | — | — | workable |
| Stripe | AI Engineer | 1155 SA Transformation | Chicago | false | — | — | greenhouse |

```json
{
  "recordType": "job",
  "ats": "ashby",
  "companyName": "Openai",
  "title": "Research Engineer",
  "department": "Research",
  "location": "San Francisco",
  "isRemote": false,
  "employmentType": "FullTime",
  "postedAt": "2025-04-05T00:03:20.653000+00:00",
  "url": "https://jobs.ashbyhq.com/openai/240d459b-696d-43eb-8497-fab3e56ecd9b",
  "applyUrl": "https://jobs.ashbyhq.com/openai/240d459b-696d-43eb-8497-fab3e56ecd9b/application",
  "salaryMin": 250000,
  "salaryMax": 445000,
  "salaryCurrency": "USD",
  "salaryInterval": "year",
  "salarySource": "ats",
  "descriptionText": "As a Research Engineer here, you will be responsible for building AI systems…",
  "uid": "c79e36daaff2a69d"
}
```

Company summary example:

```json
{
  "recordType": "companySummary",
  "companyName": "Stripe",
  "ats": "greenhouse",
  "openJobs": 727,
  "postedLast7Days": 45,
  "postedLast30Days": 238,
  "remoteJobs": 117,
  "topLocations": [{"name": "Singapore", "count": 46}, {"name": "Dublin", "count": 43}, {"name": "Bengaluru", "count": 34}]
}
```

*(Figures from a test run in October 2026; live numbers change daily.)*

The Console has three ready-made views: **Jobs**, **Company hiring summaries** and **Errors**.

### Which signalscouter Actor should I use?

| | [Career Site Jobs Scraper](https://apify.com/signalscouter/career-site-jobs-scraper) | [Greenhouse Jobs Scraper](https://apify.com/signalscouter/greenhouse-jobs-scraper) | [Ashby & Lever Jobs Scraper](https://apify.com/signalscouter/ashby-lever-jobs-scraper) |
|---|---|---|---|
| Best for | Many companies on **mixed ATSes** in one run | Companies on **Greenhouse** | Startups on **Ashby or Lever** |
| ATS coverage | 12 ATSes (Greenhouse, Lever, Ashby, Workday, Workable, Teamtailor and more) | Greenhouse only | Ashby and Lever only |
| Job rows | Core fields in one shared schema | Departments, offices, custom fields, **structured pay ranges** | **Structured compensation and equity**, team and workplace type |
| Domain discovery | Yes | Yes, plus board-name check | Yes |
| Company summary | Basic hiring summary | Richer: departments, offices, salary coverage | Richer: teams, workplace mix, median salary, equity share |
| Price per 1,000 jobs | **$1.00** | **$1.50** | **$1.50** |
| Price per 1,000 company summaries | **$2.00** | **$5.00** | **$5.00** |

**Rule of thumb:** use Career Site Jobs Scraper for broad coverage at the lowest price. Use a single-ATS Actor when you need pay ranges, compensation, equity or richer summaries. You're on the **Career Site Jobs Scraper** page now. For Workday sites with more than 2,000 jobs, use the [Workday Jobs Scraper](https://apify.com/signalscouter/workday-jobs-scraper).

### How much does it cost?

The Actor uses **pay-per-event** pricing. Current prices are always on the **Pricing** tab. Current rates:

- **$1.00 per 1,000 jobs** (`job-result` event)
- **$0.002 per company summary** (`company-summary` event, optional; turn it off to skip it; in monitoring mode, only for companies with new jobs)

Error records (companies that couldn't be resolved) are **never charged**. In monitoring mode (`onlyNewJobs`) you pay only for **new** jobs, plus one company summary for each company that has at least one new job. A monitoring run that finds nothing new pushes no job or summary rows and costs **$0** (there's no start fee). If Apify restarts or migrates a run, or you resurrect it, jobs and summaries already in its dataset are skipped, not pushed or charged again. Use **Max jobs per company** and **Max total jobs** to cap the cost of a run. Apify's free monthly credits are enough to try it.

### FAQ

**Is scraping job postings legal?**
This Actor reads only **public job postings** that employers publish so they get seen and shared. It uses each ATS's public job-board endpoint. It doesn't log in, bypass access controls, or collect personal data about candidates or employees. It respects robots.txt and adds a polite delay per host; for example, SmartRecruiters is excluded because its robots.txt disallows general crawlers. You're responsible for how you use the data. Check the platforms' terms and the laws that apply to you (e.g. GDPR if you combine it with personal data).

**Which ATS platforms are supported?**
Greenhouse, Lever, Ashby, Workday, Workable, Recruitee, Personio, Rippling, Teamtailor, Pinpoint, BambooHR and Breezy HR. For each one the Actor uses the public job feed or careers-page endpoint that the platform serves for the company's own careers site. SmartRecruiters is excluded because its robots.txt disallows general crawlers. Jobvite, iCIMS and others are on the roadmap. Open an issue to tell us which one you need most.

**What are the limits?**

- **Board size:** most ATS feeds return a company's whole board in one call.
- **Workday:** lists don't include descriptions, so with descriptions on, the Actor makes one extra request per matched job. For Workday, the company summary counts the jobs the run actually fetched, within your caps.
- **Rippling:** returns titles, departments and locations, but no descriptions or salary.
- **BambooHR and Breezy HR:** lists don't include descriptions, so with descriptions on, the Actor makes one extra request per matched job (BambooHR posting dates and pay also come from that request, so for BambooHR the company summary's date and salary counts only cover jobs whose details were fetched within your caps).
- **Pinpoint:** doesn't publish a posting date, so `postedAt` is empty and the "posted within N days" filter doesn't remove Pinpoint jobs. Postings the employer has excluded in its robots.txt are skipped.
- **Teamtailor:** the feed has no employment type. Remote status comes from the career site's remote setting.
- **Posting dates:** some boards give dates only roughly (Workday reports "Posted 3 Days Ago") or not at all (Pinpoint, Rippling).

**Can I schedule it and get only new jobs?**
Yes. Turn on **Only new jobs since last run** and create a **Schedule** in Apify Console. The seen-jobs state is kept in a named key-value store (`career-jobs-state`) in your account. Delete that store to start fresh.

**How do I integrate it?**
Use the **Apify API** or the official JavaScript and Python clients. You can also connect it with **Make, Zapier, n8n, Google Sheets, Slack, webhooks**, or the **Apify MCP server** for AI agents. Results export to JSON, CSV, Excel, XML or RSS.

**A company wasn't found, or the data looks wrong.**
Pass the careers URL or `ats:slug` directly. If that still fails, open an issue with the input you used and we'll look at it.

### Support and feedback

Built and maintained by **signalscouter** ([apify.com/signalscouter](https://apify.com/signalscouter), <signalscouter.data@gmail.com>). Found a bug, need another ATS, or want a custom field? Open a ticket on the **Issues** tab. We read every issue and aim to reply quickly. If the Actor saves you time, a review on the Store helps others find it.

# Actor input Schema

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

One per line. Accepts a company domain (stripe.com), a careers or ATS board URL (https://jobs.lever.co/palantir, https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite, https://savills.teamtailor.com), or an explicit 'ats:slug' (greenhouse:stripe, ashby:openai, workable:huggingface, teamtailor:savills, pinpoint:davies, bamboohr:savanta, breezy:end). Domains are auto-detected.

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

Keep only jobs whose title, department or team contains at least one keyword (case-insensitive). Leave empty for all jobs.

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

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

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

Keep jobs whose location contains any of these strings, e.g. London, Germany, Remote.

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

Keep jobs whose department contains any of these strings, e.g. Engineering, Sales.

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

Keep only jobs flagged remote by the ATS or with 'Remote' in the location.

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

0 = no date filter. Jobs without a posting date are kept.

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

Remembers job IDs in a named key-value store and outputs only jobs not seen in previous runs. Ideal for scheduled daily runs and alerts.

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

Plain-text description (and salary parsed from pay-transparency text). Turn off for faster, lighter runs.

## `includeHtml` (type: `boolean`):

Adds descriptionHtml alongside descriptionText.

## `includeCompanySummary` (type: `boolean`):

One extra record per company: open jobs, posted in last 7/30 days, remote share, top departments and locations, new since last run.

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

0 = unlimited.

## `maxTotalJobs` (type: `integer`):

Stop pushing results after this many jobs. 0 = unlimited.

## `workdayFetchDetails` (type: `boolean`):

Workday lists don't include descriptions; this makes one extra request per job. Turn off for large Workday boards.

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

How many companies to process at the same time.

## `requestDelaySecs` (type: `number`):

Politeness delay per host. Lever asks for 1s.

## Actor input object example

```json
{
  "companies": [
    "stripe.com",
    "https://jobs.ashbyhq.com/openai",
    "lever:palantir",
    "workable:huggingface"
  ],
  "remoteOnly": false,
  "postedWithinDays": 0,
  "onlyNewJobs": false,
  "includeDescription": true,
  "includeHtml": false,
  "includeCompanySummary": true,
  "maxJobsPerCompany": 0,
  "maxTotalJobs": 0,
  "workdayFetchDetails": true,
  "maxConcurrency": 5,
  "requestDelaySecs": 1
}
```

# Actor output Schema

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

Dataset opened on the Jobs view; rows with recordType=job are job postings.

## `companySummaries` (type: `string`):

Dataset opened on the Company hiring summaries view; rows with recordType=companySummary carry the hiring signals (open jobs, posted last 7/30 days, remote share, top departments/locations).

## `errors` (type: `string`):

Dataset opened on the Errors view; rows with recordType=error are inputs that couldn't be resolved (ATS_NOT_DETECTED / BOARD_NOT_FOUND), never charged.

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

RUN_SUMMARY record: totals for jobs, companies, unresolved inputs and HTTP requests.

# 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": [
        "stripe.com",
        "https://jobs.ashbyhq.com/openai",
        "lever:palantir",
        "workable:huggingface"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("signalscouter/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": [
        "stripe.com",
        "https://jobs.ashbyhq.com/openai",
        "lever:palantir",
        "workable:huggingface",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("signalscouter/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": [
    "stripe.com",
    "https://jobs.ashbyhq.com/openai",
    "lever:palantir",
    "workable:huggingface"
  ]
}' |
apify call signalscouter/career-site-jobs-scraper --silent --output-dataset

```

## MCP server setup

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