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

Get every open job from company career sites on Greenhouse, Lever, Ashby, SmartRecruiters, Recruitee and Workable. Paste names or URLs; the platform is detected. Salary, remote, filters, only-new mode. $2 per 1,000 jobs.

- **URL**: https://apify.com/reverend_sal/career-site-jobs-api.md
- **Developed by:** [North Eng](https://apify.com/reverend_sal) (community)
- **Categories:** Jobs, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $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, SmartRecruiters, Recruitee & Workable

Get **every open job straight from company career sites** in one clean, uniform format. Paste company names or career site URLs; the Actor finds which applicant-tracking system (ATS) each company uses and reads its **official public job-board feed** — the same data the company's own careers page shows. No browser or proxy is needed, so a typical run of a few companies finishes in seconds.

**Supported platforms:** Greenhouse · Lever (US and EU) · Ashby · SmartRecruiters · Recruitee · Workable

**Price:** $2 per 1,000 jobs, nothing for companies that cannot be found. **Not supported:** Workday, iCIMS, Taleo and other systems (see [limits](#how-it-works-and-limits)).

### Quick start

1. Paste company names or career site URLs into **Companies** (one per line).
2. Optionally add filters (keywords, locations, remote only, posted within N days).
3. Click **Start**. Jobs appear in the **Output** tab within seconds; export them as JSON, CSV, Excel or XML, or read them through the API.

### What you can do with it

- **Job boards and aggregators:** pull fresh postings from hundreds of companies on a schedule.
- **Recruiting and sales intelligence:** see who is hiring, for which teams, where, and at what pay.
- **Job alerts:** turn on **Only new jobs** and schedule the Actor: each run returns only jobs you have not received yet, and you pay only for those.
- **Market research:** salary ranges, remote share, hiring by department or country, across many companies.

### Input

| Field | What it does |
|---|---|
| **Companies** | One per line: a board name (`stripe`), `platform:name` (`lever:spotify`, `ashby:ramp`, `smartrecruiters:Ubisoft2`), a board URL (`https://boards.greenhouse.io/figma`, `https://jobs.lever.co/spotify`, `https://jobs.ashbyhq.com/ramp`, `https://bunq.recruitee.com`, `https://apply.workable.com/huggingface`) or a company careers page that links to its board. |
| **Platform for bare names** | Detect automatically (default) or force one platform for bare names. |
| **Keywords / Exclude keywords** | Whole-word title match (optionally also the description). `java` does not match `JavaScript`. |
| **Locations, Remote only, Departments, Employment types, Posted within (days)** | Narrow the results. Many boards do not state the employment type; such jobs are kept when Full-time is selected. |
| **Only new jobs** + **Memory key** | Deliver only jobs not delivered before with the same key. |
| **Max jobs / Max jobs per company** | Cost control. Per-company limits keep the newest jobs. |
| **Include description text / HTML** | Full description as plain text (default) and/or HTML. |

Example input:

```json
{
    "companies": ["stripe", "https://jobs.lever.co/spotify", "ashby:ramp", "https://www.figma.com/careers/"],
    "keywords": ["engineer", "data scientist"],
    "locations": ["Remote", "New York", "London"],
    "postedWithinDays": 14,
    "maxJobs": 500
}
```

### Output

One row per job, with the same fields for every platform:

```json
{
    "id": "ashby:ramp:a5c7c68a-33f9-4f78-929e-29b58f26d22a",
    "jobId": "a5c7c68a-33f9-4f78-929e-29b58f26d22a",
    "title": "People Business Partner, G&A",
    "company": "Ramp",
    "companySlug": "ramp",
    "platform": "ashby",
    "department": "People & Talent",
    "team": "People Business",
    "location": "New York, NY (HQ)",
    "locations": ["New York, NY (HQ)"],
    "country": "USA",
    "workplaceType": "hybrid",
    "remote": false,
    "employmentType": "full-time",
    "employmentTypeRaw": "FullTime",
    "salaryMin": 134000,
    "salaryMax": 236000,
    "salaryCurrency": "USD",
    "salaryPeriod": "year",
    "salaryText": "$134K - $236K",
    "salarySource": "platform",
    "postedAt": "2026-10-28T22:02:17.374Z",
    "updatedAt": null,
    "url": "https://jobs.ashbyhq.com/ramp/a5c7c68a-33f9-4f78-929e-29b58f26d22a",
    "applyUrl": "https://jobs.ashbyhq.com/ramp/a5c7c68a-33f9-4f78-929e-29b58f26d22a/application",
    "requisitionId": null,
    "descriptionText": "ABOUT RAMP\n\nRamp is building the smart infrastructure for finance teams…",
    "scrapedAt": "2026-10-29T10:22:34.796Z"
}
```

- Every job row has the same fields on every platform; a value the board does not publish is `null` (never missing or guessed).
- **`id`** is stable across runs (`platform:board:jobId`): use it to deduplicate or join.
- **`workplaceType`** is `remote`, `hybrid`, `onsite` or `null` (not stated); **`remote`** is `true` only for fully remote jobs.
- **`employmentType`** is normalised to `full-time`, `part-time`, `contract`, `internship` or `temporary`; the platform's own label is in `employmentTypeRaw`.
- **Salary:** `salarySource` is `platform` when the ATS publishes a structured range (Ashby, Lever, Recruitee, Greenhouse pay transparency), or `description` when a range with a currency was found in the job text. Otherwise the salary fields are `null` — nothing is guessed.
- Companies that cannot be found or loaded get one **free error row** (`input`, `errorCode`, `error`): `NOT_FOUND` (no board with that name), `PLATFORM_NOT_DETECTED` (a careers page with no supported board on it), `UNSUPPORTED_PLATFORM` (Workday, iCIMS, Taleo and similar), `FETCH_FAILED` (the board did not answer), `INVALID_INPUT`, `BLOCKED_URL` (private or local addresses).

The run summary (key-value store record `OUTPUT`) lists, per company, the platform found, jobs on the board, jobs matching your filters, jobs delivered and — in only-new mode — new and closed jobs.

### Only new jobs (scheduled runs)

With **Only new jobs** on, the Actor remembers the IDs it delivered under your **Memory key** (in your named key-value store `career-site-jobs-api-state`). The next run with the same key skips them, so a daily schedule returns just the new postings. The first run delivers every matching job. Jobs that disappeared from a board since the last run are listed under `closedJobs` in the run summary, free. Jobs not delivered because of a limit are not remembered, so they come in a later run. Use a different key for each separate search, and avoid running two runs with the same key at the same time.

### Pricing

Pay per result: **$0.002 per job** ($2 per 1,000 jobs), plus Apify's standard Actor start fee ($0.00005 per run start). Error rows, duplicates, jobs skipped by your filters and jobs already delivered in only-new mode are **not charged**. Set a maximum cost per run in the run options: the Actor stops cleanly when it is reached, and you are never charged for a job that was not saved. Platform usage (compute) is included in the price.

What a run costs is simply the number of jobs delivered × $0.002 (+ $0.00005 per start):

| Example run | Jobs delivered | Cost |
|---|---|---|
| 3 companies, 20 newest jobs each | 60 | $0.12 |
| Any search with **Max jobs** = 500 | at most 500 | at most $1.00 |
| Full export of a board with 1,000 open jobs | 1,000 | $2.00 |
| Scheduled **Only new jobs** run | only jobs that appeared since the last run | $0.002 each; $0.00005 when nothing is new |

### Use it from code, automations or an AI agent

- **One API call, jobs back as JSON:** `POST https://api.apify.com/v2/acts/<this Actor>/run-sync-get-dataset-items?token=<YOUR_TOKEN>` with the input as the body (for runs that finish within 5 minutes; see the API tab for the JavaScript, Python and CLI clients).
- **A feed for your job board:** save your input as a task, schedule it, and read `https://api.apify.com/v2/actor-tasks/<task id>/runs/last/dataset/items?status=SUCCEEDED&format=json` (or `format=csv`, `xml`, `xlsx`) to get the latest run's jobs.
- **AI agents (MCP):** the Actor works with the Apify MCP server. The cost of a run is predictable: at most `maxJobs` × $0.002, and companies that are not found are free.
- **Integrations:** Make, Zapier, n8n, Slack, Google Drive and webhooks are available from the Integrations tab.

### How it works and limits

- Data comes from each platform's official public job-board API (for example Greenhouse's job board API, Lever's postings API, Ashby's posting API). Only jobs the company publishes publicly are available.
- A bare name is checked on all six platforms; the board with the most jobs wins and other matches are noted in the summary (`alsoFoundOn`). If two companies share a name, pass `platform:name` or the board URL instead.
- A careers page URL on the company's own domain works when the page links to its board (embedded Greenhouse, Lever, Ashby, SmartRecruiters, Recruitee or Workable links). Otherwise the Actor tries the domain name as the board name.
- SmartRecruiters lists carry no description: the Actor reads each delivered job's detail record (only for jobs you receive). Greenhouse pay ranges work the same way (switch off "Greenhouse pay ranges" for maximum speed).
- Not supported: Workday, iCIMS, Taleo, SuccessFactors, Personio and other systems (such URLs get a free error row); LinkedIn or Indeed listings.
- Runs are restart-safe: if Apify migrates the run to another server, delivered jobs are not delivered or charged again.

### FAQ

**Do I need proxies or an API key?** No. All feeds are public and served by the platforms' APIs.

**How fresh is the data?** Live: every run reads the boards at that moment, so a job posted a minute ago is included and a closed job is not.

**Can I search all companies at once, without a list?** No. This Actor reads the boards of the companies you name, live and in full. If you need a search across every company, a job database product is the better fit; if you know your target companies, this is the cheaper and fresher way.

**My run returned 0 jobs. Why?** The run's status message says why: the boards had no open jobs, your filters removed them all (it lists which filter removed how many), or, in only-new mode, nothing was new since the last run.

**How many companies per run?** Up to 1,000. Bare names are checked on all six platforms, so URLs or `platform:name` entries are a little faster.

**Why is a company not found?** Its board name may differ from its brand (for example `Ubisoft2` on SmartRecruiters). Open the company's careers page, click a job, and paste that URL or the board URL.

# Actor input Schema

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

One company per line: a job board name ("stripe"), a name with its platform ("lever:spotify", "ashby:ramp", "smartrecruiters:Ubisoft2"), a job board URL (https://jobs.lever.co/spotify, https://boards.greenhouse.io/figma, https://jobs.ashbyhq.com/ramp, https://bunq.recruitee.com, https://apply.workable.com/huggingface) or a company careers page that links to one. Bare names are looked up on every supported platform.

## `platform` (type: `string`):

Which platform bare names ("stripe") are on. "Detect automatically" checks all of them and uses the board with the most jobs. URLs and "platform:name" entries always use their own platform.

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

Keep jobs whose title contains ANY of these words or phrases (whole words, case-insensitive: "java" does not match "JavaScript"). Leave empty for all jobs.

## `searchInDescription` (type: `boolean`):

Also match the keywords against the job description, not only the title.

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

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

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

Keep jobs whose location or country contains ANY of these, e.g. "London", "Germany", "US", "Remote".

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

Keep only jobs marked as remote by the platform, or with "remote" in the title or location.

## `includeHybrid` (type: `boolean`):

With "Remote jobs only", also keep hybrid jobs.

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

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

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

Keep only these employment types. Jobs whose board does not state a type (common on Greenhouse) are kept when Full-time is selected and left out otherwise. Empty = all.

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

Keep jobs posted (or, where the platform gives no posting date, updated) in the last N days. 0 = any date. Jobs without any date are dropped when this is set.

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

Remember the jobs delivered and, on later runs with the same memory key, deliver only jobs you have not received yet (you pay only for new ones). The first run delivers every matching job. Ideal for scheduled runs. Jobs that closed since the last run are listed in the run summary (OUTPUT) for free.

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

Name of the memory used by "Only new jobs". Runs with the same key share it; use different keys for separate searches. Stored in your named key-value store "career-site-jobs-api-state".

## `maxJobs` (type: `integer`):

Stop after this many jobs in total (all companies). You pay $0.002 per job delivered, so 1,000 jobs cost at most $2. This is a hard cap on the cost of a run.

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

Newest jobs first. 0 = no per-company limit. Tip: keep it small (e.g. 20) for a quick, cheap first run.

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

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

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

Also add the description as HTML (descriptionHtml), as published on the board.

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

Greenhouse publishes pay-transparency ranges only on each job's own record: read it for every Greenhouse job delivered (one extra request per job, a little slower).

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

How many boards and job detail pages are loaded at the same time.

## Actor input object example

```json
{
  "companies": [
    "stripe",
    "https://jobs.lever.co/spotify",
    "https://jobs.ashbyhq.com/ramp"
  ],
  "platform": "auto",
  "keywords": [],
  "searchInDescription": false,
  "excludeKeywords": [],
  "locations": [],
  "remoteOnly": false,
  "includeHybrid": false,
  "departments": [],
  "employmentTypes": [],
  "postedWithinDays": 0,
  "onlyNew": false,
  "stateKey": "default",
  "maxJobs": 1000,
  "maxJobsPerCompany": 20,
  "includeDescription": true,
  "includeDescriptionHtml": false,
  "greenhouseSalary": true,
  "maxConcurrency": 8
}
```

# Actor output Schema

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

Job postings with title, company, location, remote flag, employment type, salary, dates, description and apply link.

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

Per company: platform detected, jobs on the board, jobs matching the filters, jobs delivered, new and closed jobs (only-new mode), 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": [
        "stripe",
        "https://jobs.lever.co/spotify",
        "https://jobs.ashbyhq.com/ramp"
    ],
    "maxJobsPerCompany": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("reverend_sal/career-site-jobs-api").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": [
        "stripe",
        "https://jobs.lever.co/spotify",
        "https://jobs.ashbyhq.com/ramp",
    ],
    "maxJobsPerCompany": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("reverend_sal/career-site-jobs-api").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": [
    "stripe",
    "https://jobs.lever.co/spotify",
    "https://jobs.ashbyhq.com/ramp"
  ],
  "maxJobsPerCompany": 20
}' |
apify call reverend_sal/career-site-jobs-api --silent --output-dataset

```

## MCP server setup

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

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/TL5R6dUmiZbczzjnK/builds/zbtHe3GaRYHeO2iXx/openapi.json
