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

Search over 200,000 open jobs from about 6,900 company career sites on Greenhouse, Ashby and Recruitee in one run, plus live Lever and Workable boards for companies you list. Fuzzy title match, salary, seniority and remote filters. $0.90 per 1,000 jobs; daily-feed repeats and closed jobs are free.

- **URL**: https://apify.com/fernandosord/career-site-jobs.md
- **Developed by:** [Plainsource](https://apify.com/fernandosord) (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 $0.90 / 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 API returns open jobs straight from the public job boards that companies use on their career sites: Greenhouse jobs, Lever jobs, Ashby, Workable and Recruitee. Search a daily snapshot of about 210,000 open Greenhouse, Ashby and Recruitee jobs on about 6,900 company boards in one run, or list companies and fetch their boards live, which is the only way to include Lever and Workable.

Each row is one job with its title, company, location, remote or hybrid flag, salary when the employer publishes it, seniority, posting date and apply link. It suits recruiters, sourcers, job-board and newsletter builders, and anyone who wants a jobs API for specific companies without visiting each careers page. Nothing is scraped from LinkedIn or Indeed, and there is no login or proxy involved.

### What data can Career Site Jobs API extract?

| Field | Notes |
|---|---|
| `id` | Stable id in the form `ats:company:jobId`. Use it to join runs. |
| `ats`, `company`, `companyId` | The platform, the company name and its board id on that platform. |
| `title`, `department`, `team`, `employmentType` | As published. Some fields are empty on some platforms. |
| `workplaceType` | `remote`, `hybrid`, `onsite`, or `null` when the employer did not say. |
| `location`, `locations`, `countryCode` | Primary location, plus every location after duplicate merging. |
| `salary` | `{ min, max, currency, period, text }` or `null`. Ashby and Lever often publish it, Greenhouse rarely. |
| `seniority` | `intern`, `entry`, `mid`, `senior`, `lead`, `manager`, `director` or `executive`, worked out from the title. `mid` means no level word was found. |
| `postedAt`, `updatedAt` | ISO dates from the platform. Lever and Workable do not publish update times. |
| `url`, `applyUrl`, `otherUrls` | Job page, application page, and the URLs of merged duplicates. |
| `duplicateGroupId` | The same value for the same company and title. |
| `changeStatus` | `new`, `updated` or `closed` in daily feed mode, otherwise `null`. |
| `description` | Plain text, only when **Include full description** is on. |
| `scrapedAt` | When the run produced the row. |

### How to use Career Site Jobs API

1. Open the Actor on Apify and click **Try for free**.
2. Under **Job title keywords**, enter the roles you want, for example `data engineer`. Word order does not matter: "senior data engineer" matches "Data Engineer, Senior".
3. Add filters if you need them: **Locations** (a city, `remote` or a two-letter country code), **Workplace type**, **Posted within (days)**, **Seniority**, **Only jobs with salary info**.
4. Leave **Companies** empty to search the whole snapshot, or paste career-site URLs, `ats:id` pairs such as `greenhouse:stripe`, or plain board ids to search only those companies live.
5. Set **Max results** and click **Start**. The newest jobs come first.
6. For a daily feed, turn on **Only new or changed jobs**, keep the same **State key**, and put the run on a schedule.

### How much does it cost to use the jobs API?

You pay per event.

| Event | Price |
|---|---|
| Actor start | $0.001 per GB of run memory (the default is 512 MB, so $0.001 per run) |
| Job result | $0.0009 per open job returned |

Closed-job rows are free, and in daily feed mode jobs you already received are not returned or charged again.

Worked examples, from the prices above:

- 1,000 jobs in one run: $0.001 + 1,000 x $0.0009 = $0.901.
- 200 remote data jobs posted this week: $0.001 + 200 x $0.0009 = $0.181.
- The prefilled example below returned 22 jobs: $0.001 + 22 x $0.0009 = $0.0208.

If you set a maximum cost per run, the run stops adding results when it is reached.

### Input

Three searches you can paste into the JSON input tab. The first is the Actor's prefilled example (it returned 22 jobs on 2026-10-28):

```json
{
  "keywords": ["software engineer"],
  "postedWithinDays": 30,
  "companies": ["https://boards.greenhouse.io/airbnb", "lever:palantir", "ashby:ramp"],
  "maxItems": 50
}
```

Remote data and analytics engineering jobs posted this week:

```json
{ "keywords": ["data engineer", "analytics engineer"], "workplaceTypes": ["remote"], "postedWithinDays": 7, "maxItems": 200 }
```

A daily feed of new roles at a fixed list of companies (repeat jobs are not charged):

```json
{ "companies": ["greenhouse:airbnb", "lever:palantir", "ashby:ramp", "workable:huggingface"], "onlyNew": true, "reportClosed": true, "stateKey": "my-companies" }
```

A company can be given as a board URL, as `ats:id` (`greenhouse:stripe`), or as a bare id (`stripe`). Bare ids are looked up in the index and then tried on every platform. If nothing is found, the run log and the `SUMMARY` record say so.

Other filters: `excludeKeywords`, `ats` (limit to some platforms), `exactMatch`, `dedupe` (on by default), `descriptionKeywords` (the description must mention one of the phrases; this refetches the matching boards live, so keep the title keywords specific) and `includeDescription`.

### Output

This is a real row from the prefilled run on 2026-10-28. The `salary.text` field (the salary as the employer wrote it) is left out here.

```json
{
  "id": "ashby:ramp:29a5da77-37f0-402a-92bc-072778e77ed8",
  "ats": "ashby",
  "companyId": "ramp",
  "company": "Ramp",
  "title": "Software Engineer, Forward Deployed",
  "department": "Engineering",
  "team": "Engineering",
  "employmentType": "FullTime",
  "workplaceType": "hybrid",
  "location": "New York, NY (HQ)",
  "locations": [
    "New York, NY (HQ)",
    "San Francisco, CA"
  ],
  "countryCode": "US",
  "salary": {
    "min": 168000,
    "max": 285000,
    "currency": "USD",
    "period": "year"
  },
  "postedAt": "2026-10-22T19:20:16.077Z",
  "updatedAt": null,
  "url": "https://jobs.ashbyhq.com/ramp/29a5da77-37f0-402a-92bc-072778e77ed8",
  "applyUrl": "https://jobs.ashbyhq.com/ramp/29a5da77-37f0-402a-92bc-072778e77ed8/application",
  "seniority": "mid",
  "duplicateGroupId": "938d10d16b1a",
  "changeStatus": null,
  "scrapedAt": "2026-10-28T21:59:11.886Z"
}
```

Every run also writes a `SUMMARY` record to its key-value store with the source used (`snapshot` or `live`), the number of jobs scanned and matched, and warnings such as a board that was not found.

### What people use a jobs API for

- Sourcing and hiring signals: which companies are hiring for a role right now, and where.
- A daily feed of new roles for a niche job board or newsletter.
- Salary research, using the `hasSalary` filter to keep only postings that publish pay.
- Tracking a list of target companies, including closed roles.
- Entry-level and intern searches, using the seniority filter.

### FAQ

#### Which job boards are covered?

Greenhouse, Lever, Ashby, Workable and Recruitee. Companies on Workday, iCIMS, SmartRecruiters, Taleo, SuccessFactors or their own systems are not covered. SmartRecruiters is left out on purpose, because its API host's robots.txt only allows LinkedIn's crawler.

#### Why are Lever and Workable only searched when I list companies?

Their APIs are slow to sweep (Lever asks for one request per second, Workable answers bursts with errors), so they are not in the daily snapshot. Add the companies you want to **Companies** and they are fetched live. A list of 100 Lever or Workable companies takes a couple of minutes.

#### How fresh is the data?

With a company list, every board is fetched live during your run. Without one, the results come from a snapshot that a companion Actor rebuilds once a day at 06:00 UTC, so a job posted this morning may show up the next day. Descriptions are always fetched live, because the snapshot does not carry them.

#### What if a company is missing?

The index is the set of boards discovered so far, not every company. Add the company to **Companies** and it is fetched live.

#### How accurate is the seniority field?

It is a guess from the title. A title like "Engineer II" lands in `mid`.

#### How do I get only new jobs every day?

Turn on **Only new or changed jobs**, use the same **State key** every time, and schedule the Actor in Apify Console. Add **Report closed jobs** to also get rows marked `closed`. The Actor keeps its memory in a key-value store named `ats-jobs-state` in your account.

#### How do I use it with the API, Make, Zapier or n8n?

Call it with the Apify API or client, for example `client.actor("fernandosord/career-site-jobs").call({ keywords: ["data engineer"], maxItems: 100 })`. In Make, Zapier or n8n, use the Apify integration to run the Actor and read its dataset. A common setup is a daily schedule with **Only new or changed jobs** turned on, sending results to Google Sheets, Slack or a webhook.

#### Can an AI agent use it?

Yes. Add the Actor to the Apify MCP server (`https://mcp.apify.com`). The agent sees the input schema, so it can search jobs by title, location and salary.

#### Is it allowed?

The Actor reads job postings that employers publish for wide distribution, through documented public endpoints. It does not log in, use proxies or bypass anything. Lever is fetched at one request per second, as its robots.txt asks. If you are an employer and want your board removed from the index, open an issue on the Issues tab and it will be removed within 14 days.

### Other Actors by this developer

- [Website Change Monitor](https://apify.com/fernandosord/website-change-monitor): watch a company's careers page and get the lines or links that changed.
- [Open Places Business Leads](https://apify.com/fernandosord/open-places-leads): business lists by city or country from open data, with phone, website and email.
- [SEC EDGAR API](https://apify.com/fernandosord/sec-edgar-api): filings, financials and insider trades for public companies you are researching.
- [Website Tech Stack Detector](https://apify.com/fernandosord/website-tech-stack-detector): a BuiltWith alternative that lists the technologies a site uses.
- [WHOIS & DNS Lookup](https://apify.com/fernandosord/domain-profile-whois-dns): domain age, SSL and email security for a list of domains.

# Actor input Schema

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

A job matches if its title contains every word of any one phrase, in any order. "senior data engineer" matches "Data Engineer, Senior". Common abbreviations are expanded (Sr, Eng, SWE, PM, ML). Leave empty to return every job.

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

Drop jobs whose title matches any of these phrases, e.g. "intern" or "manager".

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

Keep jobs whose location contains any of these, e.g. "Toronto", "Berlin", "remote". Two-letter country codes (US, CA, GB, DE, ES, MX...) also work.

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

Only keep jobs the ATS marks with one of these types. Greenhouse often leaves this unspecified, so including "unspecified" avoids dropping those jobs.

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

Only jobs first published in the last N days. 0 means any date.

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

Optional. Limit the search to these companies and fetch their boards live. Required for Lever and Workable boards, which are not in the daily snapshot. Accepts board URLs (https://jobs.lever.co/palantir), "ats:id" (greenhouse:airbnb), or a plain board id (stripe). Unknown ids are tried on every supported ATS and reported if not found.

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

Only search these platforms. Empty means all five. Without a company list, Lever and Workable are skipped (they are only fetched live for listed companies).

## `seniorityLevels` (type: `array`):

Detected from the job title. "mid" means no level word was found.

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

Keep only postings that publish pay (common on Ashby and Lever, rare on Greenhouse).

## `descriptionKeywords` (type: `array`):

Keep jobs whose description matches any of these phrases, e.g. "visa sponsorship" or "Kubernetes". Turns on description fetching for matched boards, so runs take longer.

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

Add the plain-text job description to each result.

## `exactMatch` (type: `boolean`):

Match keywords as exact substrings instead of word-by-word.

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

Many companies post the same role once per city. When on, these become one row with all locations listed and the other URLs in otherUrls.

## `onlyNew` (type: `boolean`):

Return only jobs that are new or changed since your last run with the same state key. You are not charged for jobs you already received. Use this with a schedule for a daily feed.

## `reportClosed` (type: `boolean`):

With "Only new or changed jobs", also output jobs that disappeared since the last run, marked changeStatus: closed. These rows are not charged.

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

Keeps separate "seen jobs" memories for different searches. Use one key per saved search.

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

Upper limit on paid results per run. Newest jobs come first.

## Actor input object example

```json
{
  "keywords": [
    "software engineer"
  ],
  "excludeKeywords": [],
  "locations": [],
  "workplaceTypes": [],
  "postedWithinDays": 30,
  "companies": [
    "https://boards.greenhouse.io/airbnb",
    "lever:palantir",
    "ashby:ramp"
  ],
  "ats": [],
  "seniorityLevels": [],
  "hasSalary": false,
  "descriptionKeywords": [],
  "includeDescription": false,
  "exactMatch": false,
  "dedupe": true,
  "onlyNew": false,
  "reportClosed": false,
  "stateKey": "default",
  "maxItems": 50
}
```

# Actor output Schema

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

One row per matching job posting.

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

Data source, counts and warnings for the run.

# 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": [
        "software engineer"
    ],
    "postedWithinDays": 30,
    "companies": [
        "https://boards.greenhouse.io/airbnb",
        "lever:palantir",
        "ashby:ramp"
    ],
    "maxItems": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("fernandosord/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 = {
    "keywords": ["software engineer"],
    "postedWithinDays": 30,
    "companies": [
        "https://boards.greenhouse.io/airbnb",
        "lever:palantir",
        "ashby:ramp",
    ],
    "maxItems": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("fernandosord/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 '{
  "keywords": [
    "software engineer"
  ],
  "postedWithinDays": 30,
  "companies": [
    "https://boards.greenhouse.io/airbnb",
    "lever:palantir",
    "ashby:ramp"
  ],
  "maxItems": 50
}' |
apify call fernandosord/career-site-jobs --silent --output-dataset

```

## MCP server setup

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