# Career Page Jobs API: Greenhouse, Lever, Ashby & more (`reeboolabs/career-page-jobs-api`) Actor

Get open jobs straight from company career boards (Greenhouse, Lever, Ashby, SmartRecruiters, Workable, Recruitee). Enter company names, domains or board URLs. Filter by title, location, remote and date. Salary included when clearly stated. $2 per 1,000 jobs, only for jobs you receive.

- **URL**: https://apify.com/reeboolabs/career-page-jobs-api.md
- **Developed by:** [REEBOO Labs](https://apify.com/reeboolabs) (community)
- **Categories:** Jobs, Lead generation, Automation
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$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 Page Jobs API

Open job listings straight from company career boards, as clean structured data. Give us company names, domains or career board links. We read the boards' own public JSON feeds and return one tidy row per open job: title, location, remote flag, department, salary when the board states it clearly, posting date, apply link and description.

No browser, no proxy, no login, no scraping of rendered pages. You pay only for jobs that end up in your dataset.

### Supported job boards

Greenhouse, Lever, Ashby, SmartRecruiters, Workable and Recruitee. That is the full list for this version. Workday and other systems are not covered yet, and a company that runs its own custom career site will not be found.

### Why use it

- **Direct from the source.** Data comes from the boards' own public feeds, not from a third-party copy.
- **Pay only for what you keep.** The price is per job delivered, after your filters and after de-duplication. Companies we cannot find cost nothing.
- **Incremental mode.** Turn on "Only jobs not delivered before" and a scheduled run delivers, and charges for, only the new postings since your last run.
- **Names, domains or links.** Type `gitlab`, `gitlab.com` or `https://boards.greenhouse.io/gitlab`. For names and domains we look for the board on all six systems and keep every match that has open jobs.
- **Salary only when it is clear.** Structured pay is used when the board provides it, unless it is a yearly-sized amount labelled monthly (20,000 or more in USD, EUR, GBP or CHF), which we do not deliver. Otherwise we read a pay range from the description text only when exactly one range is stated and its period is written in English right next to it (for example "per hour", "annual salary:", or a "Salary:" label in front of an amount of 20,000 or more, read as yearly). Both number styles are understood: `€3.000 - €4.000` and `$3,000 - $4,000` mean thousands, `€12,50` and `$12.50` mean cents. These stay empty: several different ranges; bonus, stipend or equity ranges; company-size figures such as "$5-$10 billion"; working hours such as "40 hours per week"; numbers grouped with spaces; and amounts that do not fit their period (such as 3-4 per month, or more than 2,000,000 per year), which also means a very high amount in a low-value currency such as JPY or INR can stay empty. This is a rule on the text, so an unusual wording can still be misread.

### Pricing

$0.002 per job delivered, which is **$2 per 1,000 jobs**. No start fee. Example: 250 jobs cost $0.50. You can also set a maximum cost per run in Apify; when it is reached the run stops and keeps everything delivered so far. In our test at the limit the run ended with the status Succeeded; Apify may also end such a run itself, and in that case the jobs already delivered stay in your dataset. With `onlyNewJobs`, each batch of up to 50 jobs is written to the memory just before it is delivered, so a later run does not deliver or charge those jobs again. The trade-off: if a run is killed in the instant between the two steps, up to 50 jobs of that batch can be missing from your dataset and will not be offered again until the memory expires after 120 days.

### Input

| Field | What it does |
|---|---|
| `companies` | Board URLs, company names or domains. One entry per company. |
| `titleKeywords` / `excludeTitleKeywords` | Keep titles containing any of these / drop titles containing any of these. |
| `locationKeywords` | Keep jobs whose location or country contains any of these texts. |
| `remoteOnly` | Remote jobs only (board flag, else the word "Remote" in location or title). |
| `postedWithinDays` | Only jobs first published in the last N days. Jobs without any date are dropped when set. |
| `includeDescription` | Plain-text description (default on). `includeHtml` adds the job's HTML (Greenhouse sends its markup escaped; we convert it back to normal tags). |
| `onlyNewJobs` + `stateKey` | Only jobs not delivered by earlier runs with the same state key. |
| `maxJobsPerCompany` | Default 1000, counted after filters. |
| `maxJobsTotal` | Stop after this many jobs in total. |

Example:

```json
{
  "companies": ["gitlab", "https://jobs.ashbyhq.com/ramp", "zoox.com"],
  "titleKeywords": ["engineer"],
  "remoteOnly": true,
  "postedWithinDays": 30,
  "onlyNewJobs": true,
  "stateKey": "remote-engineering",
  "maxJobsPerCompany": 50
}
```

### Output

One dataset item per job. This is a real item from a run of this Actor on Apify on 2026-11-05 (description shortened here):

```json
{
  "source": "lever",
  "company": "zoox",
  "companySlug": "zoox",
  "jobId": "e8a190f0-7a89-4c11-a6a0-452ff6234749",
  "title": "Administrative Business Partner, Floater",
  "department": "Executive Office",
  "team": "Executive Office",
  "location": "Foster City, CA",
  "country": "US",
  "isRemote": false,
  "employmentType": "Full-time",
  "salaryMin": 123000,
  "salaryMax": 168000,
  "salaryCurrency": "USD",
  "salaryPeriod": "year",
  "postedAt": "2026-10-06T17:27:44.932Z",
  "updatedAt": null,
  "applyUrl": "https://jobs.lever.co/zoox/e8a190f0-7a89-4c11-a6a0-452ff6234749/apply",
  "jobUrl": "https://jobs.lever.co/zoox/e8a190f0-7a89-4c11-a6a0-452ff6234749",
  "description": "Zoox is building the future of transportation. To do that, we’re also building an incredible crew—united by ou ...",
  "scrapedAt": "2026-11-05T09:41:07.813Z"
}
```

A second record named `OUTPUT` in the key-value store lists, per company, which board it was found on, how many jobs were found, how many were delivered, and which companies could not be found.

### FAQ

**A company was not found. Why?** It may use a system we do not support, or a different board name. Paste its career board link instead of the name. Not-found companies are listed in `OUTPUT` and are never charged.

**Can a name match the wrong company?** Yes. Short names can exist on several boards, for example a different company with the same board name. Board links are exact; use them when precision matters.

**What if a job's description cannot be loaded?** On SmartRecruiters the description is a separate request per job. If that request fails (after retries), the job is not delivered and not charged, it is counted as `detailFailed` in `OUTPUT`, and with "only new jobs" it is offered again next time. That job still counts toward your per-company limit in that run.

**Which fields differ between boards?** Each board publishes different details. Missing values are `null`. SmartRecruiters publishes no links in its job list, so for those rows `jobUrl` and `applyUrl` are built from the company and job id (the board's detail data replaces them when included; the built links follow the board's public job-page format and the apply link carries the board's own `?oga=true` suffix). `country` is given as the board gives it (a code on some boards, a name on others). `employmentType` is the board's own wording. Some boards have no remote field, so we also look at the location and title.

**Where does salary come from?** Lever, Ashby, Recruitee and SmartRecruiters provide structured pay on some postings. For other postings we read the description. A bare `$` is not labelled USD because it can mean several currencies; the amounts and period are still filled when clear. On SmartRecruiters, pay needs "Include job description" on, because it sits on the detail page.

**How does "only new jobs" work?** We remember the jobs we delivered for your state key in a named storage of your account. A job we did not deliver (for example because you hit a limit) is still new next time. Jobs not seen for 120 days are forgotten, so a very old reappearing job may be delivered again.

**How fast is it?** It depends on the board and on where it runs. In one test on a local computer (not on Apify), 4,378 jobs from 13 boards on Greenhouse, Lever and Ashby took about 30 seconds. SmartRecruiters with descriptions needs one extra request per job, so it is slower. Treat both as rough guides, not a guarantee; we have not measured speed on the Apify platform.

**Limits.** Six board systems only. Public listings only. We are polite to the boards: at most 3 parallel requests per host, with retry and back-off (we honour a Retry-After wait; without one we wait 1 s, 2 s, 4 s ... and give up after 5 tries). You are responsible for how you use the data and for following the terms of the boards and the law that applies to you.

**Something looks wrong.** Open an issue from the Actor page with the company and what you expected.

# Actor input Schema

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

One entry per company. Use a career board URL (for example https://boards.greenhouse.io/gitlab or https://jobs.lever.co/zoox), or just a company name or domain (for example gitlab or gitlab.com) and we look for its board on all six supported systems. Entries we cannot find are listed in the OUTPUT record and are not charged.

## `titleKeywords` (type: `array`):

Keep a job only if its title contains at least one of these words or phrases. Not case sensitive. Leave empty to keep all titles.

## `excludeTitleKeywords` (type: `array`):

Drop a job if its title contains any of these words or phrases. Not case sensitive.

## `locationKeywords` (type: `array`):

Keep a job only if its location or country contains one of these texts, for example Berlin, Germany or United States. Not case sensitive.

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

Keep only remote jobs. Remote is taken from the board when it says so, otherwise from the word Remote in the location or title.

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

Keep only jobs first published in the last N days. Jobs with no date at all are dropped when this is set. Leave empty for no limit.

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

Add the full description as plain text. Turn off for a lighter, faster list. On SmartRecruiters this also saves one extra request per job, but salary is then only filled when the board lists it without the detail page (usually it is not).

## `includeHtml` (type: `boolean`):

Add a descriptionHtml field with the job HTML (real tags on every board) next to the plain-text description. Needs Include job description.

## `onlyNewJobs` (type: `boolean`):

Deliver (and pay for) only jobs that earlier runs with the same State key have not delivered yet. Good for daily or weekly schedules.

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

Name of your job memory for Only new jobs. Use one key per search, for example berlin-engineering. Runs with the same key share memory; letters, digits and hyphens only.

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

Upper limit of delivered jobs for each entry in Companies, counted after all filters.

## `maxJobsTotal` (type: `integer`):

Stop the run after this many jobs have been delivered. Leave empty for no limit. The maximum cost per run you set in Apify also stops the run.

## Actor input object example

```json
{
  "companies": [
    "gitlab.com",
    "https://jobs.lever.co/zoox"
  ],
  "titleKeywords": [
    "engineer",
    "data"
  ],
  "excludeTitleKeywords": [
    "intern"
  ],
  "locationKeywords": [
    "germany",
    "london"
  ],
  "remoteOnly": false,
  "includeDescription": true,
  "includeHtml": false,
  "onlyNewJobs": false,
  "stateKey": "default",
  "maxJobsPerCompany": 20
}
```

# Actor output Schema

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

One row per open job found on the requested company career boards.

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

Per-company result of the run: boards found, jobs returned and any company not resolved.

# 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": [
        "gitlab",
        "https://jobs.ashbyhq.com/ramp"
    ],
    "maxJobsPerCompany": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("reeboolabs/career-page-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": [
        "gitlab",
        "https://jobs.ashbyhq.com/ramp",
    ],
    "maxJobsPerCompany": 20,
}

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

```

## MCP server setup

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