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

Open jobs straight from company career sites: Greenhouse, Lever, Ashby, Workable, Recruitee and Workday. Give links, board names or just company websites. Salary, remote, department, posting date, new-jobs monitor and a hiring summary per company.

- **URL**: https://apify.com/saliga/career-site-jobs.md
- **Developed by:** [Salih Küçükkayış](https://apify.com/saliga) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 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.

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

Get every open job straight from company career sites: **Greenhouse, Lever, Ashby, Workable, Recruitee and Workday**. Give job board links, short names like `greenhouse:airbnb`, or **just company websites** (`stripe.com`): the Actor finds the job board on the site. Each job comes with title, location, remote, department, team, job type, posting date, **salary** (when the company publishes it), apply link and the full description as plain text.

Two things job board scrapers don't give you:

- **New jobs monitor**: turn on *Only new jobs since the last run*, schedule the Actor daily or weekly, and get only the openings that appeared since the previous run. Perfect for job alerts, recruiting and sales triggers.
- **Hiring summary per company**: switch *Output* to *Companies* and get one row per company: open jobs, jobs posted in the last 7 and 30 days, departments, top locations, remote share and, with the monitor on, new and removed jobs since the last run. A ready-made hiring signal for sales and investment teams.

**$1 per 1,000 jobs**, **$3 per 1,000 company rows**, plus $0.005 per run. Companies whose job board can't be found are free. No login, no API key, no proxies.

### Quick start

Press **Start** with the example input:

```json
{
  "companies": ["https://jobs.ashbyhq.com/ramp", "greenhouse:airbnb"]
}
```

Each job becomes one dataset row (a Ramp job as the live board returns it, description shortened):

```json
{
  "id": "34413f8d-26bf-4bbc-8ade-eb309a0e2245",
  "title": "Security Engineer, Cloud",
  "company": "Ramp",
  "ats": "ashby",
  "url": "https://jobs.ashbyhq.com/ramp/34413f8d-26bf-4bbc-8ade-eb309a0e2245",
  "applyUrl": "https://jobs.ashbyhq.com/ramp/34413f8d-26bf-4bbc-8ade-eb309a0e2245/application",
  "location": "New York, NY (HQ)",
  "locations": ["New York, NY (HQ)", "Remote (Canada)"],
  "country": "USA",
  "remote": true,
  "workplaceType": "hybrid",
  "department": "Engineering",
  "team": "Backend",
  "employmentType": "FullTime",
  "postedAt": "2026-04-07T17:12:35.753Z",
  "salaryMin": 211400,
  "salaryMax": 290600,
  "salaryCurrency": "USD",
  "salaryPeriod": "year",
  "salaryText": "$211.4K - $290.6K",
  "description": "ABOUT RAMP\n\nRamp is building the smart infrastructure ...",
  "input": "https://jobs.ashbyhq.com/ramp",
  "boardUrl": "https://jobs.ashbyhq.com/ramp"
}
```

### What you can give it

| Input | Example |
|---|---|
| Greenhouse | `https://boards.greenhouse.io/airbnb`, `job-boards.greenhouse.io/airbnb`, `greenhouse:airbnb`, EU: `greenhouse-eu:acme` |
| Lever | `https://jobs.lever.co/spotify`, `lever:spotify`, EU: `jobs.eu.lever.co/acme`, `lever-eu:acme` |
| Ashby | `https://jobs.ashbyhq.com/ramp`, `ashby:ramp` |
| Workable | `https://apply.workable.com/huggingface`, `workable:huggingface` |
| Recruitee | `https://bunq.recruitee.com`, `recruitee:bunq` |
| Workday | `https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite` |
| Company website | `stripe.com`, `https://www.notion.so` |

For a website, the Actor reads the home page and the usual career pages (/careers, /jobs, ...) and looks for a link to or an embed of a supported job board. If none is linked, it tries the company name from the domain (`stripe.com` → `stripe`) on Greenhouse, Ashby, Lever and Workable, and a shortened name (`datadoghq.com` → `datadog`) on Greenhouse and Workable when the board's company name is an exact match; a Greenhouse or Workable board is only accepted when its company name matches. Each row says how the board was found in `detectedBy` (`input`, `link` or `name-match`).

### Filters

*Job title contains*, *Job title does not contain*, *Location contains*, *Department contains*, *Remote jobs only* and *Posted within (days)*. Filtered-out jobs are not charged.

### Pricing

| Event | Price | When |
|---|---|---|
| Job | $1 / 1,000 | each job row in the results |
| Company | $3 / 1,000 | each company row (Companies output) |
| Run start | $0.005 | per run, at the default 512 MB memory |

Cost examples:

| Job | Cost |
|---|---|
| All 165 open jobs at Ramp | $0.17 |
| 200 companies, about 40 jobs each | $8.01 |
| Hiring summary of 1,000 companies | $3.01 |
| Daily monitor of 100 companies, 30 new jobs a day | $0.04 a day |

Set a spending limit on the run (*Max total charge* in Apify) and the Actor stops cleanly at it.

### Use it from code, n8n, Make or an AI agent

```bash
curl -X POST "https://api.apify.com/v2/acts/saliga~career-site-jobs/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"companies": ["stripe.com", "greenhouse:airbnb"], "keywords": ["engineer"], "remoteOnly": true}'
```

### How it reads the job boards

- **Public job board APIs** for Greenhouse, Lever, Ashby, Workable and Recruitee: the same feeds these systems publish so that companies can show their jobs on their own sites. One request returns a company's whole board, which is why it's fast and cheap.
- **Workday** career sites through the JSON the site itself loads. The site's robots.txt is checked first and the board is skipped if it disallows it.
- **Polite by design**: one request at a time per site, a pause between requests (Lever's 1 second is respected), and robots.txt is honoured for every career page and Workday site. No login, no cookies, no CAPTCHA solving, and no personal data such as recruiter emails.

### Limits

- Supported systems: Greenhouse, Lever, Ashby, Workable, Recruitee and Workday. Companies on other systems (iCIMS, Taleo, SuccessFactors, SmartRecruiters, Personio, Teamtailor, custom pages) return an error row, free.
- Workday lists stop at 2,000 jobs per career site and give rough dates ("Posted 3 Days Ago"; `postedAtApproximate` is true for "30+ days"). Turn on *Workday: read each job page* for descriptions, exact dates and all locations.
- Salary is filled when the job board has a salary field (Lever, Ashby, Recruitee) or, with descriptions on, when the description states a range like "$150,000 - $190,000", "152,000 USD - 241,500 USD" or "€3.500 - €4.500 per month" (Workday: with *Workday: read each job page* on).
- In the Companies output, `totalJobsOnBoard` is the job board's own count (when the board gives one) even if *Max jobs per company* cut the list; `openJobs` counts the jobs that were read and passed the filters.
- The same job board given twice (for example as a link and as a short name) is read and charged once.
- With the monitor on, if a board has more jobs than *Max jobs per company*, new and removed jobs can be incomplete; raise the limit for full tracking.
- Matching by company name can pick the wrong board when two companies share a name (Lever and Ashby don't publish a company name to check). Give the job board link when you know it.

### FAQ

**Why are some websites "not found"?** The site doesn't link to a supported job board from its home page or the usual career pages, or robots.txt disallows those pages, or the company uses another system. The `error` field says which. These rows are free.

**How does the monitor know what's new?** It keeps the job ids of each company in a named key-value store in your Apify account (`career-site-jobs-monitor-<name>`). Use a different *Monitor name* per project.

**Other tools from the same developer**: [Video to Scenes](https://apify.com/saliga/video-to-scenes), [Audio to Text](https://apify.com/saliga/audio-to-text), [Document to Markdown](https://apify.com/saliga/document-to-markdown), [Bulk HTTP Requests](https://apify.com/saliga/bulk-http-requests), [Website Screenshot + Change Detection](https://apify.com/saliga/website-screenshot-change-detection), [Keyword Search Volume](https://apify.com/saliga/keyword-search-volume).

# Actor input Schema

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

One per line: a job board link (boards.greenhouse.io/airbnb, jobs.lever.co/spotify, jobs.ashbyhq.com/ramp, apply.workable.com/huggingface, bunq.recruitee.com, nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite), a short form (greenhouse:airbnb, lever:spotify, ashby:ramp, workable:huggingface, recruitee:bunq) or just the company website (stripe.com): the job board is found on the site.

## `outputMode` (type: `string`):

Jobs: every open job with salary, location, department, date and description. Companies: one row per company with open job count, jobs posted in the last 7 and 30 days, departments, locations and, with the monitor on, new and removed jobs since the last run.

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

Keep jobs whose title contains any of these words (not case-sensitive), e.g. engineer, sales. Empty keeps all.

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

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

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

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

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

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

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

Keep only jobs marked remote by the job board or with Remote in the location.

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

Keep only jobs first published in the last N days. Jobs without a date are dropped when this is set. Workday gives only rough dates ("Posted 3 Days Ago").

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

Remember the jobs of each company and return only jobs that were not there in the previous run with the same monitor name. Schedule the Actor daily or weekly to get new openings as they appear. The first run returns all jobs.

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

Separate lists of seen jobs, e.g. one per client or project. Letters, digits and dashes.

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

The full job description as plain text. Turn off for smaller, faster results.

## `workdayDetails` (type: `boolean`):

Workday lists give title, location and a rough date only. With this on, each Workday job is opened for the description, exact date, all locations and job type (slower: one request per job).

## `guessFromCompanyName` (type: `boolean`):

If a company website does not link to its job board, try the name from the domain (stripe.com -> stripe) on Greenhouse, Ashby, Lever and Workable. A Greenhouse or Workable board is accepted only when its company name matches.

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

Stop reading a company's board after this many jobs. Workday lists stop at 2,000.

## Actor input object example

```json
{
  "companies": [
    "https://jobs.ashbyhq.com/ramp",
    "greenhouse:airbnb"
  ],
  "outputMode": "jobs",
  "remoteOnly": false,
  "onlyNewJobs": false,
  "monitorName": "default",
  "includeDescription": true,
  "workdayDetails": false,
  "guessFromCompanyName": true,
  "maxJobsPerCompany": 1000
}
```

# Actor output Schema

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

One row per job (or per company with the Companies output).

## `companies` (type: `string`):

Hiring summary per company (Companies output).

## `fullResults` (type: `string`):

Every field, including descriptions and all locations.

# 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://jobs.ashbyhq.com/ramp",
        "greenhouse:airbnb"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("saliga/career-site-jobs").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://jobs.ashbyhq.com/ramp",
        "greenhouse:airbnb",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("saliga/career-site-jobs").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://jobs.ashbyhq.com/ramp",
    "greenhouse:airbnb"
  ]
}' |
apify call saliga/career-site-jobs --silent --output-dataset

```

## MCP server setup

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

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/U3A5RIxpE1FyRqL7c/builds/hOdxoV9YdzhfbSAaH/openapi.json
