# Career Site Jobs Search: Greenhouse, Lever, Workday Jobs API (`steady-scrape/career-jobs-search`) Actor

Search open jobs by keyword across tens of thousands of company career sites (Greenhouse, Lever, Ashby, Workday, BambooHR, Workable, Recruitee, Personio, Rippling). Filter by remote, country, company, date. Optional full descriptions. No login.

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

## Pricing

from $2.00 / 1,000 job results

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 Search: Greenhouse, Lever, Workday Jobs API

Search **open jobs straight from company career sites** by keyword, the way you'd search a job board, but with no job board in between. One run searches an index of **1.3 million+ open jobs from 37,000+ company career sites** hosted on Greenhouse, Lever, Ashby, Workday, BambooHR, Workable, Recruitee, Personio, Rippling, Teamtailor, Breezy HR, Gem and Oracle Cloud HCM. The index is rebuilt daily from the employers' own public job board APIs.

- 🔎 **Keyword search on job titles**, with plural and "-ing" forms matched (`engineer` also finds "Engineering Manager") and `"exact phrases"`
- 🌍 **Filters:** remote only, country, location text, company, ATS platform, workplace type, employment type, posted within N days, salary listed
- 📄 **Full descriptions on demand**, fetched live from the employer's board for the jobs you get back (Greenhouse, Lever, Ashby and Workday; for the other platforms you get the indexed fields). Jobs that closed since the last index refresh are skipped and not charged.
- 🆕 **Only new jobs since your last run**, for daily alerts and feeds
- 🧹 **Duplicates removed** (the same title and location reposted by one employer)
- ⚡ **Fast and cheap.** A typical search finishes in seconds, at a fixed per-result price.

### Why use it?

Most job boards repost, delay or paywall listings. Career sites are the source of truth: the job is on the employer's own ATS board the day it opens and gone the day it closes. This Actor gives you **one search across thousands of those boards** without you having to know which companies use which ATS.

Need **every** job from a specific list of companies, with change tracking (new, updated, removed)? Use the companion Actor [ATS Job Feed](https://apify.com/steady-scrape/ats-job-feed). It reads the boards you name, live.

### Use cases

- **Job alerts and niche job boards:** schedule a daily run with *Only jobs not returned before* to fill a newsletter, Slack channel or job board with fresh roles ("remote data engineer", "nurse practitioner in Texas", "Rust")
- **Recruiting and sales lead generation:** find companies hiring for a role right now. Hiring for "Salesforce administrator" is a buying signal for Salesforce consultants.
- **Labor market research:** count openings by title, country, remote share, platform or company
- **AI agents and job seekers:** a clean, structured jobs API with direct apply links. No login, no scraping of job boards.

### Quick start

1. Enter one or more **keywords**, e.g. `data engineer`.
2. Optionally tick **Remote jobs only**, pick **Countries** (e.g. `US`), or set **Posted within (days)**.
3. Set **Max results** and click **Start**.
4. Download the results as JSON, CSV or Excel, or use the API.

Example input:

```json
{
  "keywords": ["data engineer", "\"analytics engineer\""],
  "remoteOnly": true,
  "countries": ["US", "CA"],
  "postedWithinDays": 14,
  "maxResults": 200,
  "includeDescriptions": false
}
```

**Keyword rules.** A job matches when its title contains *all* words of *any one* term. Words match on their start, with common endings (`engineer` matches *Engineers* and *Engineering*). Wrap a term in double quotes for an exact phrase. Use **Exclude keywords** to drop titles like `senior` or `intern`. Choose *Job title + department* to also match team names.

### Output

Each job is one dataset item with the same normalized fields as ATS Job Feed, plus search and provenance fields:

```json
{
  "jobId": "greenhouse:examplecorp:4012345678",
  "platform": "greenhouse",
  "company": "ExampleCorp",
  "title": "Senior Data Engineer",
  "department": "Engineering",
  "locationText": "Remote - US",
  "locations": [{"raw": "Remote - US", "country": "United States", "countryCode": "US", "isRemote": true}],
  "isRemote": true,
  "workplaceType": "REMOTE",
  "employmentType": "FULL_TIME",
  "salary": {"min": 150000, "max": 185000, "currency": "USD", "interval": "YEAR"},
  "postedAt": "2026-10-20T14:03:11Z",
  "jobUrl": "https://job-boards.greenhouse.io/examplecorp/jobs/4012345678",
  "applyUrl": "https://job-boards.greenhouse.io/examplecorp/jobs/4012345678",
  "descriptionText": null,
  "matchedKeyword": "data engineer",
  "source": {
    "type": "public career site (ATS job board API)",
    "indexRefreshedAt": "2026-10-26T08:41:00Z",
    "boardFetchedAt": "2026-10-26T08:15:22Z",
    "firstSeenAt": "2026-10-20T00:00:00Z",
    "descriptionFetchedLive": false
  }
}
```

*(Illustrative example.)*

The run's **OUTPUT** record summarizes the run: jobs returned, total matches, duplicates removed, and **index freshness** (when it was last refreshed, its age in hours, and jobs and sites per platform).

### Pricing

**$2 per 1,000 jobs returned** (pay per result, $0.002 per job). Platform usage is included. You set **Max results**, and the run stops cleanly at your spending limit. You're never charged for closed jobs skipped in description mode.

### FAQ

**Where does the data come from?** Only from public job postings that employers publish on their ATS career sites (the same public endpoints that power their careers pages). No login, no cookies, no private APIs.

**Does it collect personal data?** No. Postings are company job ads. We don't collect applicant, recruiter or employee data. If a posting's description names a contact person, that text is part of the employer's public ad.

**How fresh is it?** The index is refreshed daily. Each run reports `index.refreshedAt` and `index.ageHours` in OUTPUT. Each job carries `source.boardFetchedAt`. With **Include full job descriptions** on, each returned job is re-checked live, so closed jobs are dropped.

**Why doesn't it find a company I know?** We index career sites found in public directories of ATS boards. Companies on other ATSs, or with custom career sites, aren't covered. Use [ATS Job Feed](https://apify.com/steady-scrape/ats-job-feed) with the board URL for any Greenhouse, Lever, Ashby or Workday company.

**Are descriptions searched?** No. Keywords match titles (and optionally departments). Descriptions are added to results on request. Live descriptions are fetched for Greenhouse, Lever, Ashby and Workday jobs. For jobs from the other platforms, the result keeps the indexed fields and `descriptionText` stays empty (the run summary counts these under `noDetailSupport`).

**Why are some dates or locations missing?** Some platforms don't publish a posting date (BambooHR, Rippling), and Workday only gives relative ages ("Posted 30+ days ago"). For multi-location Workday jobs the list shows "N Locations" until descriptions are fetched.

### Data attribution

Part of the list of career sites we index comes from open directories:

- [LastRound AI hiring data](https://github.com/fyrosofttech/lastroundai-hiring-data) by **LastRound AI**, licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/). We used its list of company career-site identifiers; changes: validated against live boards, merged with other lists.
- [Job Board Directory](https://kalebconfer-sys.github.io/job-board-directory/) (CC0) and [job-board-aggregator](https://github.com/Riley-Dorrington/job-board-aggregator) (MIT).
- [ats-career-page-urls](https://huggingface.co/datasets/latmay/ats-career-page-urls) by latmay, licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/). We used its career-page URLs for Teamtailor, Breezy HR, Gem, Rippling and Oracle Cloud HCM; changes: reduced to board identifiers, validated against live boards, merged with other lists.
- [state-of-ats-2026](https://github.com/Kayvan-Zahiri/state-of-ats-2026) by Kayvan Zahiri (MIT): Oracle Cloud HCM career-site hosts.
- Board URLs found in the public [Common Crawl](https://commoncrawl.org/) URL index.

Job content belongs to the respective employers.

### More tools from SteadyScrape

- [ATS Job Scraper](https://apify.com/steady-scrape/ats-job-feed): every open job from the company career sites you list (Greenhouse, Lever, Ashby, Workday and more), with new and removed job tracking.
- [Remote Jobs API](https://apify.com/steady-scrape/remote-jobs-api): remote-only jobs from employer career sites, filtered by where you can work from (countries, regions, time zones).

Questions or a missing feature? Open an issue on the Actor's Issues tab.

# Actor input Schema

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

Job title search terms. A job matches if its title contains ALL words of ANY one term (word match, plurals and '-ing' forms included, e.g. 'engineer' also matches 'Engineering'). Wrap a term in double quotes for an exact phrase, e.g. ""data engineer"". Leave empty to search by the other filters only.

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

Drop jobs whose title matches any of these terms (same matching rules as keywords), e.g. 'senior', 'intern', 'manager'.

## `searchIn` (type: `string`):

Where keywords are matched. 'title' (default, most precise) or 'titleAndDepartment' (also matches the department/team name). Full job descriptions are not searched.

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

Only jobs marked remote by the employer's board or whose location says remote.

## `countries` (type: `array`):

Only jobs located in these countries. ISO 2-letter codes (US, GB, DE, CA, ...) or English names. Jobs with no detectable country are excluded when this is set.

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

Case-insensitive text match on the job location or country name, e.g. 'Berlin', 'New York', 'California', 'Remote'. A job matches if its location contains any of these.

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

Only jobs posted in the last N days (by the board's posting date; for boards without dates, the date our index first saw the job). Jobs with an unknown date are excluded when this is set.

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

Only these companies. Matches the company name, the board slug, or 'platform:slug' (e.g. 'Stripe', 'openai', 'greenhouse:figma'). Substring match, case-insensitive.

## `platforms` (type: `array`):

Only career sites hosted on these applicant tracking systems. Empty = all.

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

Only jobs with these workplace types (as stated or detected from the location). Jobs with unknown workplace type are excluded when this is set.

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

Only jobs with these employment types (when the board states one).

## `salaryOnly` (type: `boolean`):

Only jobs where the board publishes a structured salary range in its job list (mainly Lever, Ashby, Recruitee). With full descriptions on, salaries parsed from descriptions are added on top.

## `maxResults` (type: `integer`):

Maximum number of jobs to return (you are charged per returned job).

## `sortBy` (type: `string`):

'newest' = most recently posted first. 'relevance' = titles starting with / containing your exact term first, then newest.

## `includeDescriptions` (type: `boolean`):

Fetch each returned job live from the employer's board: adds the full description (HTML + text), structured salary where published, and confirms the job is still open (closed jobs are skipped, not charged). Slower; supported live for Greenhouse, Lever, Ashby and Workday. Other platforms return index data with a link.

## `onlyNewSinceLastRun` (type: `boolean`):

Skip jobs this search already returned in earlier runs (remembered per search in a key-value store in your account). Ideal for scheduled daily alerts.

## `stateKey` (type: `string`):

Name for the 'only new' memory. By default it is derived from your search filters; set this to keep the same memory after changing filters.

## `dedupe` (type: `boolean`):

Drop repeated postings of the same title + location at the same company (e.g. the same role posted on several boards of one employer).

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

Parallel requests when fetching full descriptions. Keep low to be polite to career sites.

## Actor input object example

```json
{
  "keywords": [
    "data engineer",
    "analytics engineer"
  ],
  "searchIn": "title",
  "remoteOnly": false,
  "countries": [
    "US",
    "CA"
  ],
  "salaryOnly": false,
  "maxResults": 50,
  "sortBy": "newest",
  "includeDescriptions": false,
  "onlyNewSinceLastRun": false,
  "dedupe": true,
  "maxConcurrency": 8
}
```

# Actor output Schema

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

Dataset of matching job postings, one item per job, with unified fields across all supported ATS platforms. Use /items without ?view for all fields.

## `summary` (type: `string`):

Run summary JSON: jobsPushed, matchingJobs, duplicatesRemoved, chargeLimitReached, stoppedEarly, live-description stats, and index info (refreshedAt, ageHours, stale flag, total jobs and career sites per platform).

# 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 = {
    "keywords": [
        "data engineer"
    ],
    "maxResults": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("steady-scrape/career-jobs-search").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 = {
    "keywords": ["data engineer"],
    "maxResults": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("steady-scrape/career-jobs-search").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 '{
  "keywords": [
    "data engineer"
  ],
  "maxResults": 50
}' |
apify call steady-scrape/career-jobs-search --silent --output-dataset

```

## MCP server setup

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

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/p3QGw81gudfKheA6W/builds/jL0YunTuWdeQUtfLw/openapi.json
