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

Every open job from company career sites on Greenhouse, Lever, Ashby, Workday, SmartRecruiters and Recruitee, in one clean schema with salaries where published. Paste careers URLs, websites or company names. Track new and closed jobs between runs for hiring signals.

- **URL**: https://apify.com/steadyapis/career-site-jobs.md
- **Developed by:** [Steady APIs](https://apify.com/steadyapis) (community)
- **Security:** Limited permissions
- **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

## Career Site Jobs API — Greenhouse, Lever, Ashby, Workday, SmartRecruiters & Recruitee

Get every open job from company career sites in **one clean, consistent format** — and, if you want, only the jobs that are **new** or **closed** since your last run.

Paste careers page URLs, company websites, or just company names. The Actor finds the job board, reads the company's own public job feed, and returns structured jobs with titles, locations, workplace type, dates, salary (when published), apply links and descriptions.

**$1 per 1,000 jobs.** You only pay for jobs returned — a scheduled monitor that finds nothing new costs nothing for results.

### Why use it

- **6 applicant tracking systems, one schema.** Greenhouse, Lever, Ashby, Workday, SmartRecruiters and Recruitee all come out with the same fields, so you can merge them without cleanup.
- **Hiring signals, not just listings.** Turn on *New + closed jobs* and schedule the Actor daily. You get the roles a company just opened and the ones it just filled or pulled.
- **One bad company never breaks your run.** Each company is fetched independently. Typos and dead boards are reported in `RUN_SUMMARY`; everything else still arrives. The same board pasted twice (say as a URL and as a name) is fetched and billed once.
- **Salaries where companies publish them** — including Greenhouse pay ranges (common for US roles under pay-transparency laws), Ashby, Lever, SmartRecruiters and Recruitee compensation fields.
- **Fast and cheap to run.** It reads official public job feeds — no browser, no proxies. Hundreds of jobs take seconds.
- **No double billing in monitoring mode.** If your max charge limit or per-company limit stops a run early, the jobs not yet delivered arrive on the next runs, and jobs already delivered are never charged again.

### What you can do with it

- **Sales teams:** watch target accounts for roles that signal budget ("Head of Data", "Salesforce Admin", new office locations).
- **Job boards & newsletters:** pull fresh roles from hundreds of companies on a schedule.
- **Recruiters & investors:** track hiring velocity, team growth and closed roles at portfolio or competitor companies.
- **Job seekers:** get only new remote engineering roles at the companies you care about.

### Input examples

**Just paste career pages:**

```json
{
  "companies": [
    "https://job-boards.greenhouse.io/airbnb",
    "https://jobs.lever.co/spotify",
    "https://jobs.ashbyhq.com/linear",
    "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite",
    "https://careers.smartrecruiters.com/BoschGroup",
    "https://bunq.recruitee.com"
  ]
}
```

**Websites and names work too** — `"linear.app"`, `"Ramp"`. For names we check Greenhouse, Lever, Ashby, SmartRecruiters and Recruitee (Workday needs the careers URL) and pick the board with the most open jobs; other matches are listed in `RUN_SUMMARY` so you can confirm.

**Daily monitor for new remote engineering roles:**

```json
{
  "companies": ["greenhouse:airbnb", "lever:spotify", "ashby:ramp"],
  "keywords": ["engineer", "developer"],
  "workplaceTypes": ["remote"],
  "trackChanges": "newAndClosed",
  "monitorName": "remote-eng"
}
```

Then add a schedule in Apify (e.g. every morning). Each run returns only what changed. With *Max jobs per company* set, a monitor delivers up to that many new jobs per company per run and keeps the rest for the following runs.

### Output

A real record from a test run on 29 Oct 2026 (description shortened):

```json
{
  "jobId": "smartrecruiters:BoschGroup:744000158566919",
  "ats": "smartrecruiters",
  "company": "Bosch Group",
  "title": "Internship @ Technical Functions Department",
  "department": null,
  "team": "Quality Assurance",
  "location": "Tienen, Belgium",
  "locations": ["Tienen, Belgium"],
  "country": "BE",
  "workplaceType": "onsite",
  "employmentType": "Intern",
  "postedAt": "2026-10-29T16:27:56.866Z",
  "salary": { "min": 0, "max": 800, "currency": "EUR", "period": "monthly" },
  "requisitionId": "REF277017T",
  "url": "https://jobs.smartrecruiters.com/BoschGroup/744000158566919-internship-technical-functions-department",
  "descriptionText": "Company Description\nRobert Bosch Produktie N.V., located in Tienen, Belgium, is within the…",
  "board": "https://careers.smartrecruiters.com/BoschGroup",
  "status": null,
  "firstSeenAt": null,
  "closedAt": null
}
```

`status` is `new`, `closed`, or `null` when tracking is off. Salary appears only when the company publishes it. For Greenhouse ranges the pay period isn't stated by the API, so `period` is left empty and the range is also given as text. Export as JSON, CSV, Excel or via API.

### Filters

Keywords (any, in titles — optionally descriptions), excluded words, locations (`"Germany"`, `"London"`, `"US"`, `"remote"`), departments, workplace type, and *posted within N days*. Keywords are matched by the Actor itself on the full job list. Only when you set *Max jobs per company* are Workday and SmartRecruiters asked to search on their side (faster on huge boards, but their search matches whole words only).

How terms match:

- Terms match the start of a word: `engineer` also finds "Engineering Manager".
- Short terms (3 characters or fewer, like `US`, `UK`, `AI`, `ops`) must be a whole word, so `US` won't match "Austin" or "Australia". Type `devops` or `mlops` to find those titles.
- Excluded terms must be whole words: `intern` drops "Intern – Summer 2027" but keeps "International Sales"; add `internship` to drop those too.

### Pricing

**$1.00 per 1,000 jobs returned** ($0.001 per job). No charge for companies that fail or for unchanged jobs in monitoring mode. Set *Max charge* on any run to cap spend; the Actor stops cleanly at your limit.

### Good to know

- **Workday** search results stop at 2,000 postings per board, so closed-job detection is switched off for boards that large (new jobs still work). Workday lists show relative dates ("Posted 3 days ago"); delivered jobs get exact dates when descriptions or *Exact Workday posting dates* are on. The *Posted within* filter uses the approximate date unless *Exact Workday posting dates* is on, and "Posted 30+ days ago" has no date until details are fetched.
- **SmartRecruiters** reports the date a job was last published, which can be newer than when it first opened.
- Closed-job detection runs only when a board was read completely, so you never get false "closed" alerts from a partial fetch.
- Workable, Personio and Teamtailor are on the roadmap.

### Is this legal?

The Actor reads job postings that companies publish on public job-board feeds for distribution. It does not log in, bypass protections, or collect personal data about candidates or employees. You are responsible for how you use the data; check the terms of the sites you monitor and your local laws.

### Support

Open an issue on the Actor page with the input that failed, and the run ID if you have it.

# Actor input Schema

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

One per line. Paste a careers page URL (e.g. https://job-boards.greenhouse.io/airbnb, https://jobs.lever.co/spotify, https://jobs.ashbyhq.com/linear, https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite, https://careers.smartrecruiters.com/BoschGroup, https://bunq.recruitee.com), a shorthand like greenhouse:airbnb, a company website (we look for the job board on it), or just a company name (we try Greenhouse, Lever, Ashby, SmartRecruiters and Recruitee).

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

Only keep jobs whose title contains at least one of these words or phrases. Leave empty for all jobs. On Workday and SmartRecruiters the search also runs on their side, so large boards stay fast.

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

Drop jobs whose title contains any of these whole words (e.g. intern, senior). 'intern' keeps 'International'; add 'internship' to drop those too.

## `searchDescriptions` (type: `boolean`):

Also match keywords inside the job description (needs descriptions turned on).

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

Keep jobs whose location, country or workplace matches any of these, e.g. London, Germany, US, remote. Country codes and names are interchangeable (US = United States); for US, state codes like 'San Francisco, CA' count.

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

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

## `workplaceTypes` (type: `array`):

Keep only these workplace types. 'unknown' means the board did not say.

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

Keep jobs published in the last N days. 0 = any time.

## `trackChanges` (type: `string`):

Off: return every matching job. New only: return jobs not seen in earlier runs of this monitor (great for scheduled runs). New + closed: also return jobs that disappeared since the last run (hiring signals). You only pay for jobs returned.

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

Optional. Runs with the same name share memory of seen jobs. If empty, the company list itself identifies the monitor.

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

Adds the job description as clean text. On Workday and SmartRecruiters this needs one extra request per job.

## `includeDescriptionHtml` (type: `boolean`):

Adds the original description HTML.

## `workdayExactDates` (type: `boolean`):

Workday lists only say 'Posted 3 days ago'. Turn on to fetch exact dates even without descriptions.

## `greenhouseSalary` (type: `boolean`):

Fetch published pay ranges for Greenhouse jobs (common for US roles under pay-transparency laws). One extra request per returned job; jobs you filter out are not fetched.

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

0 = no limit. Without monitoring: the most jobs returned per company (on Workday and SmartRecruiters it also limits how many are listed). With monitoring: the most NEW jobs delivered per company per run; the rest come on later runs.

## `maxJobsTotal` (type: `integer`):

Stop after saving this many jobs across all companies. 0 = no limit. Your max charge limit is always respected too.

## Actor input object example

```json
{
  "companies": [
    "https://job-boards.greenhouse.io/airbnb",
    "https://jobs.ashbyhq.com/linear",
    "lever:spotify"
  ],
  "searchDescriptions": false,
  "postedWithinDays": 0,
  "trackChanges": "off",
  "includeDescription": true,
  "includeDescriptionHtml": false,
  "workdayExactDates": false,
  "greenhouseSalary": true,
  "maxJobsPerCompany": 20,
  "maxJobsTotal": 0
}
```

# Actor output Schema

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

All jobs returned by this run, one item per job, in a single schema across Greenhouse, Lever, Ashby, Workday, SmartRecruiters and Recruitee.

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

Per-company status: which board was matched, jobs fetched, matched and returned, new and closed counts, 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://job-boards.greenhouse.io/airbnb",
        "https://jobs.ashbyhq.com/linear",
        "lever:spotify"
    ],
    "maxJobsPerCompany": 20
};

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

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

```

## MCP server setup

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