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

Jobs straight from company career sites: Greenhouse, Lever, Ashby, Workday, SmartRecruiters, Recruitee, Personio, Teamtailor, Workable, BambooHR. Search 30,000 companies or fetch your own list. Normalized location, salary, remote, dates, apply link. $2 per 1,000 jobs.

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

## Pricing

$2.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

**Jobs straight from company career sites (Greenhouse, Lever, Ashby, Workday, SmartRecruiters, Recruitee, Personio, Teamtailor, Workable, BambooHR), one normalized row per job: title, company, locations with country codes, remote/hybrid, employment type, salary, posting date, apply link and full description. Same data as career-site listing APIs, from $2 per 1,000 jobs.**

```json
{ "companies": ["airbnb", "spotify", "ramp", "BoschGroup", "bunq"], "maxItems": 100 }
```

Or search about 30,000 companies at once, without listing them:

```json
{ "titleSearch": ["Data Engineer"], "locationSearch": ["Germany"], "timeRange": "7d", "limit": 200 }
```

One output row:

```json
{
    "jobId": "8184174",
    "ats": "greenhouse",
    "company": "Airbnb",
    "companySlug": "airbnb",
    "title": "Account Manager",
    "department": "Business Development",
    "team": null,
    "employmentType": null,
    "employmentTypeRaw": null,
    "workplaceType": "HYBRID",
    "locations": ["London, United Kingdom"],
    "city": "London",
    "region": "England",
    "country": "GB",
    "countries": ["GB"],
    "salaryMin": 46000,
    "salaryMax": 54000,
    "salaryCurrency": "GBP",
    "salaryPeriod": "YEAR",
    "salaryText": null,
    "postedAt": "2026-09-09T08:35:19.000Z",
    "updatedAt": "2026-09-30T00:00:54.000Z",
    "applyUrl": "https://careers.airbnb.com/positions/8184174?gh_jid=8184174",
    "jobUrl": "https://careers.airbnb.com/positions/8184174?gh_jid=8184174",
    "careerSiteUrl": "https://job-boards.greenhouse.io/airbnb",
    "companyDomain": null,
    "descriptionText": "Airbnb was born in 2007 when two hosts welcomed three guests to their San Francisco home, and has since grown to over 5 …",
    "descriptionHtml": null,
    "scrapedAt": "2026-10-19T13:42:18.874Z"
}
```

### Compatible with the input of fantastic-jobs/career-site-job-listing-api

Switching takes one change: the Actor ID. This Actor accepts the same input fields with the same values: `timeRange` (`1h`, `24h`, `7d`, `6m`), `limit`, `titleSearch`, `titleExclusionSearch`, `locationSearch`, `locationExclusionSearch`, `descriptionSearch`, `descriptionExclusionSearch`, `organizationSearch`, `organizationExclusionSearch`, `domainFilter`, `domainExclusionFilter`, `descriptionType`, `ats`, `atsExclusionFilter`, `aiEmploymentTypeFilter`, `aiWorkArrangementFilter`, `hasSalary`, `aiHasSalary`, `datePostedAfter`, `hasNoLocation` and `remote only (legacy)`. Search terms work the same way: phrases on word boundaries, `:*` for a prefix (`Soft:*`), locations as `City, Region, Country` in English (`London, England, United Kingdom`, `New York, New York, United States`) or just a country or a city.

When the input uses those field names, the defaults are theirs too (`limit` 10, `timeRange` 7d) and each row also carries their output field names: `id`, `date_posted`, `date_created`, `organization`, `organization_url`, `locations_alt`, `locations_derived`, `cities_derived`, `regions_derived`, `countries_derived`, `location_type`, `remote_derived`, `salary`, `employment_type`, `url`, `source` (`lever.co` for Lever), `source_type`, `source_domain`, `domain_derived`, `description_text`, `description_html`, `ai_salary_currency`, `ai_salary_min_value`, `ai_salary_max_value`, `ai_salary_unit_text`, `ai_employment_type`, `ai_work_arrangement`. Set `leaderFields` to `true` or `false` to force them on or off.

Differences to know:

- **10 ATS, not 58.** `ats` values other than ours (for example `icims`, `taleo`) are accepted with a warning and match nothing.
- **AI and LinkedIn enrichment is not included.** `aiExperienceLevelFilter`, `aiVisaSponsorshipFilter`, `aiTaxonomiesFilter`, `aiLanguageFilter`, `li*` filters, `includeCompanyDetails`, `includeLinkedIn` and `removeAgency` are accepted (the run does not fail) and ignored with a warning. `ai_employment_type`, `ai_work_arrangement` and `ai_salary_*` come from the career site's own fields, not from a language model.
- **`timeRange` counts from the posting date** (`6m` = 183 days), not from the date a job entered a database.
- **`locations` is a list of strings** as the career site writes them (`["London, United Kingdom"]`), not schema.org Place objects. The normalized form is in `locations_derived`.
- **`id` is a string** (`"greenhouse-8184174"`), unique per ATS and job.

### What you get

| ATS | Companies in the built-in list | Typical career site |
|---|---:|---|
| Greenhouse | 5,553 | `job-boards.greenhouse.io/<company>` |
| BambooHR | 7,568 | `<company>.bamboohr.com/careers` |
| Workday | 5,420 | `<tenant>.wd5.myworkdayjobs.com/<site>` |
| Workable | 2,555 | `apply.workable.com/<company>` |
| Ashby | 2,167 | `jobs.ashbyhq.com/<company>` |
| Lever | 1,998 | `jobs.lever.co/<company>` |
| SmartRecruiters | 1,550 | `careers.smartrecruiters.com/<company>` |
| Personio | 1,125 | `<company>.jobs.personio.de` |
| Teamtailor | 1,094 | `<company>.teamtailor.com` |
| Recruitee | 924 | `<company>.recruitee.com` |

Every company in the list was checked against the live ATS API and had at least one open job (October 2026): about 1.6 million open jobs in total. Any other company on these 10 ATS works too: give its name, its career-page URL or `ats:slug`.

All data comes from the public job-board APIs that the career sites themselves use. No login, no browser, no proxy. Recruiter names and contact details are never output.

### Use cases

- **Job boards and aggregators:** fill a niche board (by country, remote, function or industry) with fresh jobs that link straight to the employer's application page.
- **Sales and recruiting agencies:** spot companies that are hiring for a role you serve (`titleSearch: ["Account Executive"]`, `locationSearch: ["France"]`), with the job page as the reason to reach out.
- **Market and salary research:** collect published salary ranges (US pay-transparency states, UK, EU) by title, city and company.
- **Job seekers and career coaches:** watch your target companies every day and get only the new jobs (`postedWithinDays: 1`).
- **AI agents:** one call returns structured, deduplicated jobs with descriptions, ready for matching or summarizing.

### Two ways to use it

#### Your own companies (live)

```json
{
    "companies": ["Airbnb", "https://jobs.lever.co/spotify", "greenhouse:stripe", "workday:nvidia|wd5|NVIDIAExternalCareerSite"],
    "keywords": ["engineer"],
    "locations": ["Berlin", "Remote"],
    "postedWithinDays": 30,
    "maxItems": 500
}
```

Each value can be a company name, an ATS slug (`ats:slug`), an ATS URL, or the company's own careers page (the Actor finds the embedded Greenhouse, Lever, Ashby... board). Names are matched against the built-in list first, then probed on every ATS. The `DETECTION` record of the run's key-value store shows which board was found for each value. Jobs are read live from the career site at run time, and `maxItems` is shared fairly between companies.

#### Search everything (job index)

Leave `companies` empty. The Actor searches its job index of every company in the list, refreshed daily, with your filters: `titleSearch`, `locationSearch`, `organizationSearch`, `domainFilter`, `ats`, `employmentTypes`, `workplaceTypes`, `postedWithinDays` or `timeRange`, `hasSalary`. Results come newest first. Workday, SmartRecruiters and BambooHR jobs are completed with a live call to the career site before they are saved, so their descriptions are current. If the index is not reachable, the Actor searches the built-in list live instead, largest companies first, for up to 210 seconds (`maxRunSeconds`).

### Input

| Field | Type | Description |
|---|---|---|
| `companies` | string\[] | Company names, `ats:slug`, ATS URLs or career-page URLs. Empty = search the index. |
| `keywords` | string\[] | Any of these words or phrases in the title or description (`:*` for a prefix). |
| `locations` | string\[] | City, country or `City, Region, Country`, English or local names; `Remote` matches remote jobs. |
| `remoteOnly` | boolean | Remote jobs only. |
| `workplaceTypes` | string\[] | `ONSITE`, `HYBRID`, `REMOTE`. |
| `employmentTypes` | string\[] | `FULL_TIME`, `PART_TIME`, `CONTRACT`, `INTERNSHIP`, `TEMPORARY`, `OTHER`. |
| `postedWithinDays` | integer | Jobs posted in the last N days. |
| `ats` | string\[] | Only these ATS (`greenhouse`, `lever` or `lever.co`, `ashby`, `smartrecruiters`, `workday`, `recruitee`, `personio`, `teamtailor`, `workable`, `bamboohr`). |
| `includeDescription` | boolean | Plain-text description (default `true`). |
| `includeDescriptionHtml` | boolean | HTML description (default `false`). |
| `maxItems` | integer | Maximum jobs (default 500; 10 for inputs written for the other API). |
| `maxJobsPerCompany` | integer | Cap per career board. |
| `mode` | string | `auto` (default), `companies`, `search`; `indexer` is reserved for the Actor owner. |
| `leaderFields` | boolean | Force the compatibility output fields on or off. |
| `maxRunSeconds` | integer | Stop fetching after this many seconds and keep what was found. |
| `indexStoreId` | string | Use another job index (key-value store ID). |

Plus the compatibility fields listed above.

### Output

| Field | Description |
|---|---|
| `jobId`, `ats`, `companySlug` | Job ID in the ATS, the ATS, and the board ID (Workday: `tenant\|wdN\|site`). One row per ATS + board + job ID. |
| `company`, `title`, `department`, `team` | As published by the career site. |
| `employmentType` | `FULL_TIME`, `PART_TIME`, `CONTRACT`, `INTERNSHIP`, `TEMPORARY` or `OTHER` (intern and working-student titles override a generic "Full-time"). `employmentTypeRaw` keeps the original label. |
| `workplaceType` | `ONSITE`, `HYBRID`, `REMOTE`, or `null` when the career site does not say. |
| `locations` | Locations as written by the career site. |
| `city`, `region`, `country`, `countries` | First location parsed, ISO-2 country codes of all locations. |
| `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryPeriod`, `salaryText` | Published salary (`HOUR`, `DAY`, `WEEK`, `MONTH`, `YEAR`). |
| `postedAt`, `updatedAt` | ISO 8601 UTC (Workday: day precision). |
| `applyUrl`, `jobUrl`, `careerSiteUrl` | Application link, job page, company career site. |
| `companyDomain` | Company website domain, when the ATS publishes it. |
| `descriptionText`, `descriptionHtml` | Full description. |
| `scrapedAt` | When the job was read (the index build time in search mode). |

Compatibility fields (`id`, `date_posted`, `organization`, `locations_derived`, ...) are added as described above. The run's key-value store also holds `SUMMARY` (jobs per ATS and per company, failed boards, warnings).

### Pricing

**$0.002 per job ($2 per 1,000)**, charged only for jobs saved to the dataset, after deduplication. No platform usage on top. Set a maximum cost per run in the run options and the Actor stops cleanly when it is reached.

### FAQ

**How fresh is the data?** In `companies` mode the jobs are read live at run time. In search mode they come from an index refreshed every day; jobs posted after the last refresh appear the next day, so use `companies` mode when you need the last hour.

**Why is a description search slower?** `descriptionSearch`, `descriptionExclusionSearch` and `keywords` read the full text of every job that passes the other filters, and SmartRecruiters and Workday give the text only job by job. Combine them with `titleSearch`, a location or a time range, as with other career-site APIs.

**Why do some jobs have no salary or employment type?** The Actor only outputs what the career site publishes. Greenhouse, Ashby, Lever, Teamtailor and Recruitee often publish salaries, most of all for jobs in US pay-transparency states; Workday and SmartRecruiters rarely do.

**A company is not found.** Pass its career-page URL or `ats:slug` (for example `greenhouse:airbnb`, `lever:spotify`, `workday:nvidia|wd5|NVIDIAExternalCareerSite`). If the company uses an ATS that is not supported yet, open an issue and tell us which one.

**Is this legal?** The Actor reads public job postings that companies publish so that anyone can apply, through the same public APIs their career sites use. It does not log in and does not output personal data.

# Actor input Schema

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

Company names ("Airbnb"), ATS slugs ("greenhouse:airbnb", "workday:nvidia|wd5|NVIDIAExternalCareerSite") or career-page URLs ("https://jobs.lever.co/spotify", "https://www.example.com/careers"). The ATS is detected automatically. Leave empty to search the job index instead.

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

Keep jobs whose title or description contains any of these words or phrases (case and accent insensitive, plurals tolerated). End a word with :\* for a prefix match ("engineer:\*").

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

Keep jobs in any of these places: a city, a country or "City, Region, Country" in English or the local language ("Berlin", "Germany", "Munich, Bavaria, Germany", "London, England, United Kingdom"). "Remote" matches remote jobs.

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

Keep only jobs marked remote by the career site (or whose location says remote).

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

Keep only these workplace types. Leave empty for all.

## `employmentTypes` (type: `array`):

Keep only these employment types. Leave empty for all.

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

Keep only jobs posted in the last N days. Leave empty for all open jobs.

## `ats` (type: `array`):

Keep only jobs from these ATS: greenhouse, lever (or lever.co), ashby, smartrecruiters, workday, recruitee, personio, teamtailor, workable, bamboohr. Leave empty for all.

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

Add the full job description as plain text (descriptionText).

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

Also add the description as HTML (descriptionHtml).

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

Stop after this many jobs (each saved job is one charged result). Default: 500, or 10 when the input uses the field names of other career-site APIs (as they do).

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

Optional cap per career board, so one large employer does not fill the whole result.

## `mode` (type: `string`):

Auto: live fetch when companies are given, otherwise search. Companies: live fetch of the given boards. Search: the job index over thousands of companies (live fallback over the built-in company list when the index is unavailable). Indexer: builds the job index; reserved for the Actor owner.

## `leaderFields` (type: `boolean`):

Also output the snake_case fields of other career-site job APIs (id, date_posted, organization, locations_derived, ai_employment_type, ...). On by default when the input uses their field names.

## `maxRunSeconds` (type: `integer`):

Stop fetching after this many seconds and keep what was found. The live search over the built-in company list uses 210 s when empty.

## `indexStoreId` (type: `string`):

Optional ID of a key-value store holding a job index built by the indexer mode. Leave empty to use the public index.

## `timeRange` (type: `string`):

Compatibility: jobs posted in the last 1h, 24h, 7d or 6m. Inputs that use these compatibility fields default to 7d and 10 results, as the other API does.

## `limit` (type: `integer`):

Compatibility: same as Maximum jobs.

## `titleSearch` (type: `array`):

Compatibility: words or phrases that must appear in the job title (any of them). Use :\* for a prefix ("Soft:\*").

## `titleExclusionSearch` (type: `array`):

Compatibility: drop jobs whose title contains any of these.

## `locationSearch` (type: `array`):

Compatibility: "City, Region, Country", a country or a city in English ("United Kingdom", "New York, New York, United States").

## `locationExclusionSearch` (type: `array`):

Compatibility: drop jobs in these places.

## `descriptionSearch` (type: `array`):

Compatibility: words or phrases that must appear in the title or description.

## `descriptionExclusionSearch` (type: `array`):

Compatibility: drop jobs whose title or description contains any of these.

## `organizationSearch` (type: `array`):

Compatibility: words that must appear in the company name.

## `organizationExclusionSearch` (type: `array`):

Compatibility: drop companies whose name contains any of these.

## `domainFilter` (type: `array`):

Compatibility: company domains to keep ("airbnb.com").

## `domainExclusionFilter` (type: `array`):

Compatibility: company domains to drop.

## `descriptionType` (type: `string`):

Compatibility: "text" or "html" description.

## `atsExclusionFilter` (type: `array`):

Compatibility: ATS to drop (same values as ATS platforms).

## `aiEmploymentTypeFilter` (type: `array`):

Compatibility: employment types (matched on the career site's own employment type).

## `aiWorkArrangementFilter` (type: `array`):

Compatibility: work arrangements (matched on the career site's own workplace type).

## `hasSalary` (type: `boolean`):

Compatibility: keep only jobs that publish a salary.

## `aiHasSalary` (type: `boolean`):

Compatibility: same as hasSalary.

## `datePostedAfter` (type: `string`):

Compatibility: keep jobs posted on or after this UTC date or datetime ("2026-01-01" or "2026-01-01T14:00:00").

## `hasNoLocation` (type: `boolean`):

Compatibility: keep only jobs without a recognized location.

## `remote only (legacy)` (type: `boolean`):

Compatibility: same as Remote jobs only.

## Actor input object example

```json
{
  "companies": [
    "airbnb",
    "spotify",
    "ramp",
    "BoschGroup",
    "bunq"
  ],
  "keywords": [],
  "locations": [],
  "remoteOnly": false,
  "workplaceTypes": [],
  "employmentTypes": [],
  "postedWithinDays": 30,
  "ats": [],
  "includeDescription": true,
  "includeDescriptionHtml": false,
  "maxItems": 100,
  "maxJobsPerCompany": 50,
  "mode": "auto",
  "maxRunSeconds": 210
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `summary` (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 = {
    "companies": [
        "airbnb",
        "spotify",
        "ramp",
        "BoschGroup",
        "bunq"
    ],
    "keywords": [],
    "locations": [],
    "workplaceTypes": [],
    "employmentTypes": [],
    "postedWithinDays": 30,
    "ats": [],
    "maxItems": 100,
    "maxJobsPerCompany": 50,
    "maxRunSeconds": 210
};

// Run the Actor and wait for it to finish
const run = await client.actor("praguedatalab/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": [
        "airbnb",
        "spotify",
        "ramp",
        "BoschGroup",
        "bunq",
    ],
    "keywords": [],
    "locations": [],
    "workplaceTypes": [],
    "employmentTypes": [],
    "postedWithinDays": 30,
    "ats": [],
    "maxItems": 100,
    "maxJobsPerCompany": 50,
    "maxRunSeconds": 210,
}

# Run the Actor and wait for it to finish
run = client.actor("praguedatalab/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": [
    "airbnb",
    "spotify",
    "ramp",
    "BoschGroup",
    "bunq"
  ],
  "keywords": [],
  "locations": [],
  "workplaceTypes": [],
  "employmentTypes": [],
  "postedWithinDays": 30,
  "ats": [],
  "maxItems": 100,
  "maxJobsPerCompany": 50,
  "maxRunSeconds": 210
}' |
apify call praguedatalab/career-site-jobs --silent --output-dataset

```

## MCP server setup

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