# Career Site Jobs Search: Greenhouse, Lever, Ashby (`nswk_api/career-site-jobs-search`) Actor

Search fresh jobs from 9,700+ company career sites on Greenhouse, Lever and Ashby with one keyword. Daily index, salaries, new and closed job alerts, flat JSON for AI agents. Pay only for jobs that match.

- **URL**: https://apify.com/nswk_api/career-site-jobs-search.md
- **Developed by:** [Bryan Masias](https://apify.com/nswk_api) (community)
- **Categories:** Jobs, AI
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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, Ashby

Search fresh jobs from 9,700+ company career sites with one keyword. No company list needed:
type a job title, and get every matching opening from companies that hire with **Greenhouse**,
**Lever** and **Ashby**, as flat JSON.

The index is rebuilt every day from the public job board APIs of each company. Schedule it in
**changes** mode to get only the jobs that were **added, updated or closed** since your last run.

### What it does

- Searches a daily index of company job boards by job title.
- Filters by posting date, location, country, remote/hybrid/on-site, company, department and salary.
- Returns the same fields for every board, with normalized pay ranges where companies publish them.
- Optionally fetches each job's description live, which also confirms the job is still open.
- Charges only for jobs that pass your filters.

### Input for AI agents (one field)

```json
{ "query": "senior backend engineer" }
```

That returns up to 100 matching jobs posted in the last 7 days, newest first.

A daily alert for remote data roles in North America:

```json
{
    "query": "data engineer, analytics engineer",
    "countries": ["US", "CA"],
    "workplaceType": "remote",
    "postedWithin": "24h",
    "mode": "changes",
    "monitorId": "remote-data-na",
    "maxResults": 500
}
```

`query` matches whole words in the title, in any order: `go` matches "Go Developer" but not
"Google". Separate alternatives with commas.

### Example run

The prefilled input runs in about a minute and costs a few cents at most. It returns 20 matching jobs from all three job boards.

Input:

```json
{
  "query": "backend engineer",
  "postedWithin": "7d",
  "maxResults": 20
}
```

First result (long text shortened):

```json
{
  "jobId": "greenhouse:kalepa:6238099004",
  "source": "greenhouse",
  "companyName": "Kalepa",
  "title": "Senior Backend Software Engineer - Kalepa for Operations",
  "departments": ["Engineering"],
  "location": "US or Canada",
  "url": "https://job-boards.greenhouse.io/kalepa/jobs/6238099004",
  "firstPublishedAt": "2026-10-28T23:29:10.000Z",
  "firstSeenAt": "2026-10-29T03:15:00.914Z",
  "indexAgeHours": 0.3
}
```

### Pricing examples

You pay per event:

| Event | Price | When |
|---|---|---|
| `index-query` | $0.003 | Once per run, after the index is read |
| `job-result` | $0.002 | Per job returned |
| `job-details` | $0.001 | Per live description, only with `includeDescription` |
| `closed-job` | $0.0005 | Per closed job reported in `changes` mode |

- 100 jobs: $0.003 + 100 × $0.002 = **$0.203**
- 100 jobs with descriptions: **$0.303**
- A daily alert that finds 10 new jobs: **$0.023**

Set a spending limit on the run and the Actor stops cleanly when it is reached.

### Output

```json
{
    "jobId": "greenhouse:acme:4012345",
    "source": "greenhouse",
    "companyToken": "acme",
    "companyName": "Acme",
    "title": "Senior Backend Engineer",
    "departments": ["Engineering"],
    "offices": [],
    "location": "Remote - US",
    "countryCode": "US",
    "workplaceType": "remote",
    "isRemote": true,
    "employmentType": "full-time",
    "url": "https://job-boards.greenhouse.io/acme/jobs/4012345",
    "firstPublishedAt": "2026-10-25T14:02:00.000Z",
    "postedAtEstimated": false,
    "updatedAt": "2026-10-25T14:02:00.000Z",
    "salary": { "min": 160000, "max": 200000, "currency": "USD", "period": "year" },
    "descriptionText": null,
    "changeType": null,
    "firstSeenAt": "2026-10-26T03:10:00.000Z",
    "lastSeenAt": "2026-10-26T03:10:00.000Z",
    "closedAt": null,
    "indexAgeHours": 6
}
```

- `postedAtEstimated` is `true` when the board has no posting date and `firstPublishedAt` is the day the index first saw the job.
- `companyName` is filled for Greenhouse; Lever and Ashby boards only give the board slug in `companyToken`.
- Lever and Ashby jobs carry the pay range in the index. Greenhouse pay ranges come with `includeDescription`.
- Descriptions never include emails or phone numbers.

### Monitor mode (new, updated, closed)

With `"mode": "changes"`, each job comes with `changeType`:

- `new`: matches your filters and was not in your last run.
- `updated`: its title, location, department, workplace type or salary changed.
- `closed`: it was in your earlier results and its board stopped listing it.

A job is marked closed only after two daily checks without it, and never because a board failed to
load. If you hit `maxResults` or your spending limit, the changes you did not receive are reported
on the next run. The state is kept per `monitorId`; leave it empty to get one per set of filters.

### Coverage and freshness

- Sources: Greenhouse, Lever and Ashby public job board APIs. Workday is coming next.
- About 9,700 live boards and 275,000 open jobs (Greenhouse, Lever and Ashby), checked every day. Boards are found in the Common Crawl web index every month.
- Every result carries `indexAgeHours`. If the index is more than 72 hours old the run fails
  without charging anything.

### Input reference

| Field | Type | Default | Description |
|---|---|---|---|
| `query` | string | required | Title words; commas separate alternatives |
| `postedWithin` | `24h` / `3d` / `7d` / `30d` / `any` | `7d` | Posting date window |
| `mode` | `search` / `changes` | `search` | Every match, or only changes since the last run |
| `maxResults` | integer | `100` | Up to 5,000 per run |
| `excludeKeywords` | string\[] | `[]` | Drop titles with any of these whole words |
| `locations` | string\[] | `[]` | Partial match on location and offices |
| `countries` | string\[] | `[]` | ISO alpha-2 codes |
| `workplaceType` | `any` / `remote` / `hybrid` / `onsite` | `any` | |
| `ats` | string\[] | all | `greenhouse`, `lever`, `ashby` |
| `companies` | string\[] | `[]` | Board slugs, board URLs or domains |
| `departments` | string\[] | `[]` | Partial match on department names |
| `onlyWithSalary` | boolean | `false` | Only jobs with a published pay range |
| `minSalary` | integer | none | Yearly amount in `salaryCurrency` |
| `salaryCurrency` | string | `USD` | Currency of `minSalary` |
| `includeDescription` | boolean | `false` | Live description, charged as `job-details` |
| `monitorId` | string | per filters | State name for `changes` mode |

### FAQ

**Where does the data come from?** From the public job board APIs that Greenhouse, Lever and Ashby
offer so companies can show their openings on their own sites. No login, no personal profiles.

**Why did a job disappear between the search and my click?** The index is refreshed daily. Use
`includeDescription` to confirm each job is still open at run time; jobs closed in the meantime are
skipped and not charged.

**Can I search a specific company?** Yes, with `companies`. For a full snapshot of a few companies,
the single-board Actors (Greenhouse, Lever, Ashby, Workday Jobs Monitor) are a better fit.

# Actor input Schema

## `query` (type: `string`):

Words that must all appear in the job title, as whole words and in any order. Separate alternatives with commas: "backend engineer, platform engineer". Case- and accent-insensitive.

## `postedWithin` (type: `string`):

Only jobs posted in this window. When a board has no posting date, the day the index first saw the job is used.

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

"search" returns every matching job. "changes" returns only jobs that are new, updated or closed since the last run with the same monitor ID. Schedule it daily for alerts.

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

Stop after this many jobs. You pay only for the jobs you get.

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

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

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

Keep jobs whose location or office contains any of these, e.g. "London", "Remote", "California".

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

Two-letter ISO codes, e.g. "US", "DE". Jobs whose country is unknown are dropped when this is set.

## `workplaceType` (type: `string`):

Keep only jobs with this workplace type.

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

Search only these applicant tracking systems. Empty means all.

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

Only these companies: board slugs ("stripe"), board URLs ("https://jobs.lever.co/spotify") or domains ("ramp.com").

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

Keep jobs in departments whose name contains any of these, e.g. "Engineering".

## `onlyWithSalary` (type: `boolean`):

Keep only jobs that publish a pay range.

## `minSalary` (type: `integer`):

Keep jobs whose yearly pay range reaches at least this amount, in the currency below. Hourly and monthly ranges are converted to a year.

## `salaryCurrency` (type: `string`):

Three-letter currency code for the minimum salary. Jobs paid in another currency are dropped when a minimum is set.

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

Fetch each job's description live from its board, which also confirms the job is still open. Charged per job as job-details. Emails and phone numbers are removed.

## `monitorId` (type: `string`):

Name of the saved state in changes mode. Leave empty to use one per set of filters. Letters, digits and dashes.

## Actor input object example

```json
{
  "query": "backend engineer",
  "postedWithin": "7d",
  "mode": "search",
  "maxResults": 20,
  "workplaceType": "any",
  "onlyWithSalary": false,
  "salaryCurrency": "USD",
  "includeDescription": false
}
```

# Actor output Schema

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

One item per job, in the dataset view.

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

Index age, matched jobs, and counts per change type.

# 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 = {
    "query": "backend engineer",
    "maxResults": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("nswk_api/career-site-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 = {
    "query": "backend engineer",
    "maxResults": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("nswk_api/career-site-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 '{
  "query": "backend engineer",
  "maxResults": 20
}' |
apify call nswk_api/career-site-jobs-search --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nswk_api/career-site-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/X3zdjSVEIK6DXuImw/builds/8iVI2S1nOdfguYsVK/openapi.json
