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

Every open job from company career sites on Greenhouse, Lever, Ashby, Workday, Workable, SmartRecruiters, Recruitee, Personio and BambooHR, with salaries, remote/hybrid, seniority, new-job tracking and hiring summaries.

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

## Pricing

from $1.50 / 1,000 job listings

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

**Get every open job straight from company career sites, in one clean format.** List the companies you care about and this Actor pulls their live openings directly from the hiring systems they use:

**Greenhouse · Lever · Ashby · Workday · Workable · SmartRecruiters · Recruitee · Personio · BambooHR**

Every job comes back normalized, whichever system it came from:

- **Title, department, team** and a **seniority** level (intern to executive)
- **Location**, all locations, city, region, country and **workplace type** (remote, hybrid, on-site)
- **Salary range** with currency and period, from structured pay fields or pay-transparency text
- **Employment type**, **posting date**, requisition ID, job and apply links
- **Full description** as clean text (optional)
- **"Only new jobs" mode** for scheduled runs, so you only see (and pay for) newly posted roles
- A **hiring summary per company**: open roles, top departments, remote share and newest posting

No API keys, no logins and no browser. Press **Start** to pull current openings from Stripe, Palantir and Ramp.

***

### Why this Actor exists

Job boards like LinkedIn and Indeed are noisy, duplicated and hard to scrape. The original source of almost every tech and corporate job is the company's own careers page, which runs on a handful of hiring systems with public job feeds. This Actor reads those feeds directly.

| | Job board scrapers | Single-system scrapers | **Career Site Jobs Scraper** |
|---|---|---|---|
| Source | Aggregator copies | One hiring system | **9 hiring systems, first-party** |
| Duplicates and stale posts | Common | Rare | **Rare** |
| Same output format across systems | No | No | **Yes** |
| Salary ranges | Sometimes | Sometimes | **Structured + parsed from pay-transparency text** |
| Remote / hybrid / on-site | Inconsistent | Inconsistent | **Normalized** |
| Seniority level | No | No | **Yes** |
| Company websites or names as input | No | No | **Auto-detects the careers site** |
| New-jobs-only tracking | No | Rarely | **Yes** |

***

### How it works

1. **Resolve** each company. Careers page URLs and shorthands (`greenhouse:acme`) are matched exactly. For a website (`acme.com`), the Actor reads the homepage and careers pages to find the hiring system. For a name (`Acme`), it checks the public job boards and only accepts a match when the board's company name agrees.
2. **Fetch** the company's open jobs from that system's public job feed. Paginated systems (Workday, SmartRecruiters) stop as soon as enough matching jobs are found.
3. **Normalize** locations, workplace type, seniority, employment type, dates and salaries into one schema.
4. **Filter** by title keywords, locations, remote, departments and posting date, then **deliver** one item per job.
5. **Summarize** each company in the `OUTPUT` record.

***

### Quick start

1. Paste careers page URLs, websites or company names into **Companies**.
2. Optionally add **Job title keywords** (for example `engineer`) or **Locations** (for example `London` or `remote`).
3. Press **Start** and open the **Overview** tab, or export CSV, Excel or JSON.

Example input:

```json
{
    "companies": [
        "https://boards.greenhouse.io/stripe",
        "https://jobs.lever.co/palantir",
        "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite",
        "huggingface.co",
        "Notion"
    ],
    "keywords": ["engineer", "data"],
    "locations": ["United States", "remote"],
    "maxJobsPerCompany": 200,
    "onlyNewJobs": true
}
```

**Supported careers page formats**

| Hiring system | Example |
|---|---|
| Greenhouse | `boards.greenhouse.io/acme`, `job-boards.greenhouse.io/acme`, embedded boards, EU boards |
| Lever | `jobs.lever.co/acme`, `jobs.eu.lever.co/acme` |
| Ashby | `jobs.ashbyhq.com/acme` |
| Workday | `acme.wd5.myworkdayjobs.com/External` (any `wdN` host, with or without a locale) |
| Workable | `apply.workable.com/acme` |
| SmartRecruiters | `jobs.smartrecruiters.com/Acme` |
| Recruitee | `acme.recruitee.com` |
| Personio | `acme.jobs.personio.de` or `.com` |
| BambooHR | `acme.bamboohr.com/careers` |

***

### Input

| Field | Type | Default | What it does |
|---|---|---|---|
| `companies` | array | 3 samples | Careers page URLs, `ats:slug` shorthands, websites or company names. |
| `keywords` | array | none | Keep jobs whose title contains any keyword. |
| `searchInDescription` | boolean | false | Also match keywords in descriptions. |
| `locations` | array | none | Keep jobs whose location contains any term (`remote` also matches remote jobs). |
| `remoteOnly` | boolean | false | Only fully remote jobs. |
| `departments` | array | none | Keep jobs whose department or team contains any term. |
| `postedWithinDays` | integer | 0 | Only jobs posted in the last N days (0 = all). |
| `maxJobsPerCompany` | integer | 100 | Cap per company after filters. |
| `includeDescription` | boolean | true | Include the description as clean text. |
| `onlyNewJobs` | boolean | false | Skip jobs delivered in earlier runs (per company). |
| `maxConcurrency` | integer | 4 | Companies processed in parallel. |

***

### Output

One dataset item per job. Example:

```json
{
    "company": "Ramp",
    "company_input": "https://jobs.ashbyhq.com/ramp",
    "ats": "Ashby",
    "board_url": "https://jobs.ashbyhq.com/ramp",
    "job_id": "34413f8d-26bf-4bbc-8ade-eb309a0e2245",
    "title": "Security Engineer, Cloud",
    "department": "Engineering",
    "team": "Backend",
    "location": "New York, NY (HQ)",
    "locations": ["New York, NY (HQ)", "Remote (Canada)", "Remote (US)"],
    "city": "New York City",
    "region": "NY",
    "country": "USA",
    "workplace_type": "hybrid",
    "is_remote": true,
    "employment_type": "full-time",
    "seniority": null,
    "posted_at": "2026-04-07T17:12:35Z",
    "job_url": "https://jobs.ashbyhq.com/ramp/34413f8d-26bf-4bbc-8ade-eb309a0e2245",
    "apply_url": "https://jobs.ashbyhq.com/ramp/34413f8d-26bf-4bbc-8ade-eb309a0e2245/application",
    "salary": { "min": 211400, "max": 290600, "currency": "USD", "period": "year", "text": "$211.4K - $290.6K", "source": "structured" },
    "requisition_id": null,
    "description_text": "ABOUT RAMP\n\nRamp is building the smart infrastructure for finance teams...",
    "change_status": "untracked",
    "scraped_at": "2026-10-17T19:00:00Z"
}
```

`change_status` is `new` when **Only new jobs** is on, otherwise `untracked`.

**Hiring summary** (`OUTPUT` record of the default Key-Value Store), one row per company:

```json
{
    "input": "https://jobs.ashbyhq.com/ramp",
    "company": "Ramp",
    "ats": "Ashby",
    "board_url": "https://jobs.ashbyhq.com/ramp",
    "resolved_by": "url",
    "status": "ok",
    "open_jobs_on_board": 214,
    "jobs_delivered": 100,
    "top_departments": [{ "department": "Engineering", "jobs": 41 }, { "department": "Sales", "jobs": 22 }],
    "remote_share": 0.18,
    "newest_posted_at": "2026-10-16T21:04:11Z",
    "with_salary": 96
}
```

***

### Pricing (Pay-Per-Event)

| Event | When | Price |
|---|---|---|
| `job-listing` | One job is delivered to your dataset | See the **Pricing** tab |

- You only pay for jobs that pass your filters. Companies, lookups and descriptions are included.
- With **Only new jobs** on, jobs delivered in earlier runs are skipped for free.
- Your run's maximum charge is respected: the Actor checks the remaining budget **before** each job and stops cleanly.

***

### Use cases

- **Sales intelligence**: hiring is the best buying signal. Watch target accounts for new roles in the teams you sell to.
- **Recruiting and talent intelligence**: monitor competitors' openings, salary bands and remote policies.
- **Job boards and newsletters**: build a niche board (AI, climate, remote, fintech) from first-party postings.
- **Investors and analysts**: track headcount growth by department across a portfolio.
- **Job seekers**: get a daily list of new roles at your dream companies.

***

### Use it from AI agents (MCP)

**Hosted Apify MCP server** (Claude, ChatGPT, Cursor, VS Code and any client that supports remote MCP):

```
https://mcp.apify.com?tools=AIMindTeams/career-site-jobs-scraper
```

**Local stdio server** (for example, Claude Desktop `claude_desktop_config.json`):

```json
{
    "mcpServers": {
        "career-jobs": {
            "command": "npx",
            "args": ["-y", "@apify/actors-mcp-server", "--tools", "AIMindTeams/career-site-jobs-scraper"],
            "env": { "APIFY_TOKEN": "YOUR_APIFY_TOKEN" }
        }
    }
}
```

Example prompts:

- "Which of these 10 companies are hiring data engineers in Europe, and what salary ranges do they post?"
- "List remote product manager openings at Stripe, Ramp and Notion posted this week."
- "Summarize which departments Palantir is hiring for."

***

### Use it from code

**Python** (`pip install apify-client`):

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("AIMindTeams/career-site-jobs-scraper").call(
    run_input={
        "companies": ["https://boards.greenhouse.io/stripe", "notion.so"],
        "keywords": ["engineer"],
        "remoteOnly": True,
    }
)
for job in client.dataset(run["defaultDatasetId"]).iterate_items():
    salary = job["salary"] or {}
    print(job["company"], job["title"], job["location"], salary.get("min"), salary.get("max"), job["job_url"])
```

**cURL** (run and get the items in one call):

```bash
curl -X POST "https://api.apify.com/v2/acts/AIMindTeams~career-site-jobs-scraper/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"companies": ["https://jobs.ashbyhq.com/ramp"], "maxJobsPerCompany": 20}'
```

***

### FAQ

**Which input format is most reliable?**
Careers page URLs. They are matched exactly. Websites work whenever the careers page links to or embeds a supported hiring system, and company names are matched only when the job board's own company name agrees.

**What if a company uses a system that isn't supported?**
Its row in the hiring summary says "not found" with the reason, and you are not charged for it.

**How are salaries found?**
From structured pay fields when the hiring system provides them (Ashby, Lever, Recruitee), otherwise from pay-transparency text in the description, for example "The base salary range is $185,000 - $255,000". The `salary.source` field says which.

**How fresh is the data?**
Live. Every run reads the company's current job feed directly.

**Is this legal?**
The Actor only reads the public job feeds that companies publish so their openings can be found. Use the data in line with the laws and terms that apply to you.

***

### More from AIMindTeams

- [Website Contact & Tech Stack Enricher](https://apify.com/aimindteams/website-contact-tech-enricher): emails, phones, social profiles, tech stack and email provider for any list of company websites.
- [Shopify Store Scraper & Price Monitor](https://apify.com/aimindteams/shopify-catalog-price-monitor): products, variants, prices and stock from any Shopify store, with price-drop alerts.
- [SAM.gov & SBIR Federal RFP + Attachment Deep-Parser](https://apify.com/aimindteams/govcon-rfp-deep-parser): federal opportunities with their attachments parsed.

***

### Changelog

- **1.0**: First release. Nine hiring systems, company website and name auto-detection, normalized locations, workplace type, seniority, employment type and salaries, new-jobs-only tracking and per-company hiring summaries.

# Actor input Schema

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

One per line. Accepted: careers page URLs (boards.greenhouse.io/acme, jobs.lever.co/acme, jobs.ashbyhq.com/acme, acme.wd5.myworkdayjobs.com/External, apply.workable.com/acme, jobs.smartrecruiters.com/Acme, acme.recruitee.com, acme.jobs.personio.de, acme.bamboohr.com/careers), shorthands like greenhouse:acme, company websites (acme.com) or company names (Acme). URLs are matched exactly; websites and names are detected automatically.

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

Optional. Keep jobs whose title contains any of these words or phrases, for example engineer, data scientist or account executive. Case-insensitive.

## `searchInDescription` (type: `boolean`):

Match keywords anywhere in the description too (for example a skill like Kubernetes). Slower on Workday and SmartRecruiters because each description is fetched separately.

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

Optional. Keep jobs whose location contains any of these, for example London, United States, Berlin or remote.

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

Keep only fully remote jobs.

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

Optional. Keep jobs whose department or team contains any of these, for example Engineering, Sales or Marketing.

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

Optional. 0 keeps all open jobs. Jobs without a published date are kept.

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

Upper limit of jobs delivered per company after filters. Large employers on Workday or SmartRecruiters can have thousands of openings.

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

Add the full job description as clean text (up to 20,000 characters). Turn off for smaller, faster exports.

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

Turn this on for scheduled runs: a persistent Key-Value Store (CAREER-JOBS-SEEN) remembers delivered jobs per company, so each run returns only newly posted jobs and you are never billed twice for the same job.

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

Higher is faster for long company lists.

## Actor input object example

```json
{
  "companies": [
    "https://boards.greenhouse.io/stripe",
    "https://jobs.lever.co/palantir",
    "https://jobs.ashbyhq.com/ramp"
  ],
  "searchInDescription": false,
  "remoteOnly": false,
  "postedWithinDays": 0,
  "maxJobsPerCompany": 20,
  "includeDescription": true,
  "onlyNewJobs": false,
  "maxConcurrency": 4
}
```

# Actor output Schema

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

Every job with company, hiring system, department, locations, workplace type, seniority, employment type, salary, posting date, links and the description.

## `overview` (type: `string`):

Flattened one-row-per-job view. Append \&format=csv or \&format=xlsx for spreadsheets.

## `hiringSummary` (type: `string`):

For each company: hiring system, open jobs on the board, jobs delivered, top departments, remote share, newest posting date and any errors.

# 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"
    ],
    "maxJobsPerCompany": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("aimindteams/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",
    ],
    "maxJobsPerCompany": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("aimindteams/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"
  ],
  "maxJobsPerCompany": 20
}' |
apify call aimindteams/career-site-jobs-scraper --silent --output-dataset

```

## MCP server setup

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