# Lever Jobs Scraper (`boncross/lever-jobs`) Actor

Scrape a Lever careers board for every open job. Give the board URL and get the job title, employer, location, team, contract type, posting date and the full advert, filtered by title, location or remote working. Export to CSV, JSON or Excel.

- **URL**: https://apify.com/boncross/lever-jobs.md
- **Developed by:** [BonCross API](https://apify.com/boncross) (community)
- **Categories:** Jobs, Lead generation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.70 / 1,000 job-details

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## Lever Jobs Scraper

### What is Lever Jobs Scraper?

Lever Jobs Scraper reads a company's Lever careers board from top to bottom and returns each posting with the things Lever records well: the contract type, the work arrangement (on-site, hybrid or remote) as the posting states it, each location the job is open in, and the whole advert, section by section.

You give it a board URL like `jobs.lever.co/company`. European boards on `jobs.eu.lever.co` work the same way.

What to expect:

- **The complete advert.** The introduction, each titled section (the role, responsibilities, requirements) and the closing text, joined in order. In our release run, 8 of 8 sampled adverts matched the employers' own pages.
- **Contract type on nearly every job.** It was filled on 87 of 90 jobs across three boards.
- **Work arrangement from the posting itself.** `workplaceType` is the posting's own On-site, Hybrid or Remote, and you can filter on it before anything else is read.
- **Speed that makes daily checks trivial.** 20 jobs in 2 to 8 seconds. Six boards at 50 jobs each in 4 to 9 seconds.

One thing to know before you start: few Lever employers publish pay. It was on 15 of 90 jobs across three boards, and on none of 20 at Palantir.

**BonCross Verified** means we tested this actor against the live site before release and kept the record:

- 25 of 25 live checks on its inputs and filters passed on 7 October 2026
- 3 boards run the same day, one made up: 60 jobs, 1 error row
- 8 of 8 sampled adverts matched the employers' own pages, text and link
- Charges matched the rows: 1 start, 2 boards, 60 jobs

### How to scrape a Lever board

Usually the board URL is the only thing you fill in.

1. [Create an Apify account](https://console.apify.com/sign-up). It's free to start.
2. Open [Lever Jobs Scraper](https://apify.com/boncross/lever-jobs) and paste the board URLs.
3. Optionally add title, location or work-type filters, and a cap on the number of jobs.
4. Press Start, then download the postings as Excel, CSV or JSON.

A URL from a different recruitment platform is reported as a row saying so. It doesn't stop the run.

### What data can you extract from a Lever board?

| Field | Field |
|---|---|
| 💼 Job title | 🏢 Employer |
| 📍 Location | Team or department |
| 📄 Full advert text | Advert HTML, optional |
| Contract type | Remote flag |
| 🏠 On-site, hybrid or remote | Every location a job is open in |
| 📅 Posted date | 🔗 Advert link |
| 💷 Salary from and to | Salary currency and period |
| Job id | Board name |
| Board URL | When the row was collected |

### ⬇️ Input

Paste one or more board URLs. If you have a long list, Start URLs loads it from a file or a Google Sheet.

Two caps are available and both default to 0, meaning no limit: "Max jobs per board" for each board, and "Max jobs for the whole run" over all of them.

**One board**

```json
{
  "boardUrls": ["https://jobs.lever.co/palantir"],
  "maxItemsPerBoard": 200
}
```

**Engineering roles only, several boards**

```json
{
  "boardUrls": ["https://jobs.lever.co/palantir", "https://jobs.lever.co/spotify"],
  "titleIncludes": ["engineer", "developer"],
  "maxItems": 500
}
```

**Hybrid or remote jobs only**

```json
{
  "boardUrls": ["https://jobs.lever.co/palantir"],
  "workplaceTypes": ["Hybrid", "Remote"],
  "includeUnknownWorkplace": false
}
```

**Remote postings, seven days old at most**

```json
{
  "boardUrls": ["https://jobs.lever.co/palantir"],
  "onlyRemote": true,
  "maxDaysOld": 7
}
```

**Only the postings added since your last run**

```json
{
  "boardUrls": ["https://jobs.lever.co/palantir"],
  "onlyNew": true,
  "dedupStoreName": "lever-seen"
}
```

The filters:

- **Keyword** is matched against job titles.
- **Title must contain** and **Title must not contain** keep or drop a job by words in its title. Case doesn't matter.
- **Location must contain** keeps a job only if its location includes one of your words, for example "London".
- **Work type** keeps jobs whose `workplaceType` is On-site, Hybrid or Remote. A job with no stated arrangement is kept unless "Keep jobs with no stated work type" is off. If its location or title says remote, it's treated as Remote.
- **Remote jobs only** keeps the jobs an employer marked as remote. Silence isn't read as remote.
- **Posted within (days)** keeps the last N days. 0 keeps everything.

And what goes in each row: "Include advert text" is on by default, and "Include advert HTML" adds the original HTML at the cost of much larger rows.

#### For developers

- Keys: `boardUrls`, `startUrls`, `maxItemsPerBoard`, `maxItems`, `keyword`, `titleIncludes`, `titleExcludes`, `locationIncludes`, `onlyRemote`, `workplaceTypes`, `includeUnknownWorkplace`, `maxDaysOld`, `includeDescription`, `includeDescriptionHtml`, `onlyNew`, `dedupStoreName`, `maxConcurrency`, `delayMs`.
- A board link copied after filtering on the board's own page keeps its filters: location, team, commitment, department and work type.
- `onlyNew` plus a `dedupStoreName` skips jobs an earlier run with that store name returned. An empty store name remembers nothing. The store is saved every minute during a run.
- Boards are read one after another, about 0.6 seconds each on average and under 3 seconds for the largest.
- `maxConcurrency` defaults to 8, the fastest level measured clean. Only a run that reports refusals or errors is a reason to lower it. `delayMs` (default 0) pauses before each request.
- Rows are saved as collected. A timed-out run keeps what it delivered, and a restarted run doesn't deliver or charge a job twice.

### ⬆️ Output

You get one row per posting. The Overview view shows title, employer, location, team, contract, remote flag and posting date.

```json
{
  "id": "6ed76ce8-4156-4b60-b120-403538bd66cd",
  "url": "https://jobs.lever.co/palantir/6ed76ce8-4156-4b60-b120-403538bd66cd",
  "title": "Administrative Business Partner",
  "company": "palantir",
  "location": "Singapore, Singapore",
  "department": "Administrative",
  "employmentType": "Full-time",
  "isRemote": false,
  "postedAt": "2026-08-11T17:38:11.368Z",
  "salaryMin": null,
  "salaryMax": null,
  "salaryCurrency": null,
  "salaryPeriod": null,
  "description": "A World-Changing Company\n\nPalantir builds the world’s leading software…",
  "ats": "lever",
  "tenant": "palantir",
  "scrapedAt": "2026-09-26T17:09:33.073Z",
  "boardUrl": "https://jobs.lever.co/palantir"
}
```

Notes on a few columns:

- `company` is the board id from the URL (`palantir`), not the employer's display name.
- `description` is the whole advert in order: introduction, titled sections, closing text.
- `workplaceType` is the posting's own value, and `locations` lists each location where a posting names more than one.
- `isRemote` is true or false only where the location or work type says so, and null otherwise. A hybrid job isn't remote.

### How fast is it, and how many boards fit in a run?

Palantir's board had 313 jobs when measured. Whole runs on Apify, 2 October 2026:

| Run | Result |
|---|---|
| 1 board, 20 jobs | 20 rows in 2 to 8 seconds |
| 6 boards, 50 jobs each | 200 rows in 4 to 9 seconds |
| 15 boards, 10 jobs each | 121 rows in 10 seconds |

A run inside the default one-hour timeout handles several thousand boards, so your list is unlikely to be too long. The whole board is read unless you cap it, and the job id, link, title, employer, posting date and advert were on every row.

### How much does it cost to scrape Lever jobs?

| Event | Free plan | Starter | Scale | Business |
|---|---|---|---|---|
| `actor-start`, per run | $0.001 | $0.001 | $0.001 | $0.001 |
| `board-scanned`, per careers board read | $0.0002 | $0.0002 | $0.0002 | $0.0002 |
| `job-detail`, per job with the full advert | $0.001 | $0.0009 | $0.0008 | $0.0007 |
| `job-listed`, per job without the full advert | $0.0005 | $0.00045 | $0.0004 | $0.00035 |

Enterprise plans pay the Business price.

**$1.00 per 1,000 jobs** with the full advert on the Free plan and $0.70 on Business. Rows without the advert are $0.50 per 1,000 on the Free plan, $0.35 on Business. No proxy is needed, so there's nothing extra for that, and platform usage is included.

A board of 500 jobs with full adverts: $0.001 + $0.0002 + 500 × $0.001 = **$0.5012** ($0.3512 on Business). Without the advert: $0.001 + $0.0002 + 500 × $0.0005 = **$0.2512** ($0.1762 on Business).

Each job is billed once: `job-detail` when the row has the advert (advert text or HTML switched on), `job-listed` when both are off. `board-scanned` is charged once per board.

Apify's Free plan comes with $5 of credits a month. Spent here, that's about 49 boards of 100 jobs with adverts, or about 4,900 jobs.

### Limitations

- **Lever boards only.** Another platform's URL returns a row saying so.
- **Pay is rare.** You'll find it on a minority of postings: 15 of 90 jobs across three boards, none of 20 on the Palantir board.
- **Contract type is nearly always there, not always.** 87 of 90 jobs across three boards. The others leave it empty.
- **`company` is the board id**, such as `palantir`. Map it to a display name yourself if you need one.
- **Location and team are only as good as the employer's own listing.**
- **You can't apply through this actor.**

### FAQ

#### Why is the salary empty on most jobs?

Most employers on Lever don't publish one. Where a salary is advertised you get it as numbers with currency and period.

#### How do I get only remote or hybrid roles?

Set "Work type" to Hybrid and Remote, and turn off "Keep jobs with no stated work type" if you want only jobs that state it. The second example does exactly that.

#### Why is `company` in lower case?

It's the board id taken from the URL, not a display name. If you need the proper name, map it from your own list of boards.

#### A job is open in several cities. Do I get several rows?

You get one row. `location` is the primary location and `locations` holds all of them.

#### Can I run a list of boards on different platforms?

Not here. Use [ATS Jobs Scraper](https://apify.com/boncross/ats-jobs), which works out the platform for each URL.

#### Do I need a proxy or a Lever login?

Neither is needed, and there's nothing to set up.

#### Is scraping Lever boards legal?

Companies publish these postings openly to be applied to, and no login is involved in reading them. Public data collection is generally lawful in the UK and EU. You're responsible for how you use what you collect.

### Automating it

To watch a set of companies, schedule the run and switch on "Only jobs not seen before" with a store name. Each run then gives you the new postings, which a webhook, Slack, Google Sheets, Zapier, Make or Airbyte can pick up.

From code, the run-sync-get-dataset-items endpoint of the Apify API runs the actor and returns the rows in one call. `apify-client` is available for Python and JavaScript.

AI agents reach it through the Apify MCP server at `https://mcp.apify.com`, where it appears as a callable tool.

### More from BonCross

- [Ashby Jobs Scraper](https://apify.com/boncross/ashby-jobs): Every open job on an Ashby careers board.
- [ATS Jobs Scraper](https://apify.com/boncross/ats-jobs): Jobs from any company careers page.
- [Greenhouse Jobs Scraper](https://apify.com/boncross/greenhouse-jobs): Every open job from a Greenhouse careers board.
- [Workday Jobs Scraper](https://apify.com/boncross/workday-jobs): Every open job from a Workday careers board.
- [LinkedIn Jobs Scraper](https://apify.com/boncross/linkedin-jobs-scraper): LinkedIn jobs from keywords and locations or search URLs, no login needed.
- [NHS Jobs Scraper](https://apify.com/boncross/nhs-jobs): NHS Jobs vacancies by keyword and location.

### Your feedback

If an advert comes back with a section missing, or a board won't read, tell us which one on the Issues tab in Apify Console. We read each report and reply there.

# Changelog

This Actor's version history is a separate document: https://apify.com/boncross/lever-jobs/changelog.md

# Actor input Schema

## `boardUrls` (type: `array`):

One or more Lever careers boards, for example https://jobs.lever.co/palantir

## `startUrls` (type: `array`):

The same thing, but able to bulk-load Lever boards from a file or a Google Sheet.

## `maxItemsPerBoard` (type: `integer`):

Row limit for each board. 0 means no limit.

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

Row ceiling across every board in the run. 0 means no limit.

## `keyword` (type: `string`):

Lever returns the whole board, so this is matched against job titles.

## `titleIncludes` (type: `array`):

Keep a job only if its title contains at least one of these words. Case-insensitive.

## `titleExcludes` (type: `array`):

Drop a job if its title contains any of these words. Case-insensitive.

## `locationIncludes` (type: `array`):

Keep a job only if its location contains one of these words, for example "London".

## `onlyRemote` (type: `boolean`):

Keep only jobs the employer marked as remote. Jobs where the board says nothing about remote working are dropped, not assumed.

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

Keep only jobs with these working arrangements, as the employer states them (the workplaceType column). Leave empty for all. Jobs whose board does not say are kept unless you turn off the option below; a job with no stated arrangement whose location or title says remote counts as Remote.

## `includeUnknownWorkplace` (type: `boolean`):

Only used with Work type. On (the default): jobs whose board says nothing about the arrangement are kept, because many employers never state it. Off: only jobs that state one of the chosen arrangements are kept.

## `maxDaysOld` (type: `integer`):

Keep only jobs posted in the last N days. 0 keeps everything.

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

Include the job advert as plain text.

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

Also include the advert's original HTML. Makes rows considerably larger.

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

Skip jobs returned by an earlier run that used the same store name below.

## `dedupStoreName` (type: `string`):

Named key-value store that remembers job ids between runs. Leave empty to remember nothing.

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

How many requests run at once across the whole run. The default is the fastest level measured clean; lower it only if a run reports refusals or errors.

## `delayMs` (type: `integer`):

Optional pause before each call, in milliseconds. 0 (the default) runs as fast as the parallel limit allows.

## Actor input object example

```json
{
  "boardUrls": [
    "https://jobs.lever.co/palantir"
  ],
  "startUrls": [],
  "maxItemsPerBoard": 0,
  "maxItems": 0,
  "keyword": "",
  "titleIncludes": [],
  "titleExcludes": [],
  "locationIncludes": [],
  "onlyRemote": false,
  "workplaceTypes": [],
  "includeUnknownWorkplace": true,
  "maxDaysOld": 0,
  "includeDescription": true,
  "includeDescriptionHtml": false,
  "onlyNew": false,
  "dedupStoreName": "",
  "maxConcurrency": 8,
  "delayMs": 0
}
```

# Actor output Schema

## `dataset` (type: `string`):

Every job the run returned.

## `stats` (type: `string`):

Counts 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 = {
    "boardUrls": [
        "https://jobs.lever.co/palantir"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("boncross/lever-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 = { "boardUrls": ["https://jobs.lever.co/palantir"] }

# Run the Actor and wait for it to finish
run = client.actor("boncross/lever-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 '{
  "boardUrls": [
    "https://jobs.lever.co/palantir"
  ]
}' |
apify call boncross/lever-jobs --silent --output-dataset

```

## MCP server setup

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