# Lever Jobs Scraper (`automation-lab/lever-job-postings-monitor`) Actor

Export current jobs from Lever company boards with stable IDs, recruiting fields, descriptions, application URLs, and observation timestamps.

- **URL**: https://apify.com/automation-lab/lever-job-postings-monitor.md
- **Developed by:** [Automation Lab](https://apify.com/automation-lab) (community)
- **Categories:** Jobs
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.96 / 1,000 item extracteds

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

Export current **Lever jobs** from public employer career boards into a structured Apify dataset. Supply Lever company identifiers, career-page URLs, or direct job URLs and receive stable job IDs, titles, teams, locations, workplace types, commitments, descriptions, posting URLs, application URLs, posting times, and observation timestamps.

The Actor uses Lever's anonymous structured postings interface. It does not need a browser, login, or user-supplied credential.

### What can you do with this Lever jobs scraper?

- Export a company's current Lever job board for analysis.
- Build a multi-company hiring-intelligence feed.
- Collect descriptions and application links for a job aggregator.
- Filter openings by title, team, location, workplace type, commitment, or posting date.
- Schedule recurring runs and compare stable `jobId` values between datasets.
- Feed normalized vacancies into Sheets, a database, BI tools, or an automation workflow.

The Actor reports what is public at observation time. It does not claim to maintain historical state or send alerts by itself; schedules and downstream comparisons provide that workflow.

### Who is it for?

#### Recruiting and talent intelligence teams

Track where selected employers are hiring and compare teams, locations, and workplace modes.

#### Job-board operators

Create a current structured feed from employer-selected Lever boards while preserving canonical application links.

#### Sales and market researchers

Use repeated observations of current vacancies as one public signal of team growth and geographic expansion.

#### Candidates and staffing teams

Narrow a large employer board to relevant titles, teams, locations, or remote roles.

### Why use this Actor?

- **Lever-specific inputs:** paste an identifier such as `palantir` or a public board/job URL.
- **Structured source data:** avoid fragile CSS selectors and browser rendering.
- **Useful descriptions:** receive plain text and original HTML sections.
- **Stable monitoring keys:** compare `companyIdentifier` plus `jobId` across runs.
- **Consistent filtering:** the same filters apply to board and direct-job inputs.
- **Bounded reliability:** temporary network failures receive limited retries; invalid inputs fail clearly.
- **Global and EU hosts:** both `jobs.lever.co` and `jobs.eu.lever.co` URL forms are accepted.

### What data does it extract?

| Field | Meaning |
| --- | --- |
| `jobId` | Stable Lever posting identifier |
| `companyIdentifier` | Employer's Lever site identifier |
| `sourceRegion` | `global` or `eu` Lever infrastructure |
| `title` | Public job title |
| `team` | Team or department configured by the employer |
| `location` | Primary location label |
| `allLocations` | All location labels attached to the posting |
| `workplaceType` | Lever workplace mode, such as `remote`, `hybrid`, or `onsite` |
| `commitment` | Employer-provided commitment, such as Full-time or Permanent |
| `country` | Country code when Lever supplies one |
| `postedAt` | Posting creation timestamp when available |
| `openingText` | Plain-text opening section |
| `descriptionText` | Main plain-text description |
| `descriptionBodyText` | Additional role-description body |
| `additionalText` | Benefits, policy, company, or application information |
| `fullDescriptionText` | Combined plain-text description sections |
| `descriptionHtml` | Original main description HTML |
| `descriptionBodyHtml` | Original description-body HTML |
| `additionalHtml` | Original additional-information HTML |
| `lists` | Named requirement, responsibility, or benefit sections |
| `hostedUrl` | Canonical public job URL |
| `applyUrl` | Canonical public application URL |
| `observedAt` | Timestamp when this run observed the posting |

Fields may be `null` or empty when an employer did not configure them in Lever.

### Getting started

1. Open the Actor in Apify Console.
2. Enter at least one company identifier or public Lever URL.
3. Set `maxItems` to the maximum matching jobs you need.
4. Optionally add filters.
5. Click **Start**.
6. Open the **Dataset** tab to preview, download, or integrate the records.

A simple input is:

```json
{
  "companyIdentifiers": ["palantir"],
  "maxItems": 20
}
```

### Input parameters

#### `companyIdentifiers`

A list of Lever site identifiers. The identifier is the first path segment in a board URL. For `https://jobs.lever.co/palantir`, use `palantir`.

#### `startUrls`

Public company-board or direct-posting URLs on:

- `https://jobs.lever.co/...`
- `https://jobs.eu.lever.co/...`

Board URLs export matching current jobs. A direct job URL exports that posting when it passes the same filters.

#### `maxItems`

Maximum unique matching records saved across all inputs. The default is 100 and the accepted range is 1–10,000.

#### `titleQuery`

A case-insensitive substring required in the job title. `Engineer` matches titles such as `Android Engineer` and `Software Engineer`.

#### `teams`

Case-insensitive team fragments. A posting is accepted when any supplied value appears in its team.

#### `locations`

Case-insensitive fragments checked against the primary and all additional location labels.

#### `workplaceTypes`

One or more source modes: `remote`, `hybrid`, `onsite`, or `unspecified`.

#### `commitments`

Case-insensitive commitment fragments, such as `Full-time`, `Permanent`, or `Internship`.

#### `postedAfter`

An ISO 8601 date or timestamp. Jobs without a valid creation timestamp do not pass this filter.

### Filtered Lever jobs board example

```json
{
  "startUrls": [
    { "url": "https://jobs.lever.co/spotify" }
  ],
  "titleQuery": "Engineer",
  "locations": ["London"],
  "maxItems": 20
}
```

Filters use case-insensitive substring matching. Values within one filter are OR conditions; separate filters are AND conditions.

### Multi-company monitoring example

```json
{
  "companyIdentifiers": ["palantir", "jobgether"],
  "maxItems": 400
}
```

Schedule this input daily or weekly. In your downstream system, use `companyIdentifier` plus `jobId` as the identity key and compare consecutive datasets:

- IDs present now but absent previously are newly observed postings.
- IDs absent now but present previously may be closed postings.
- Shared IDs can be compared for changed fields.
- `observedAt` records when each snapshot was collected.

A single run is a current snapshot, not a historical-change report.

### Output example

This abbreviated example reflects the current output shape:

```json
{
  "jobId": "123e4567-e89b-42d3-a456-426614174000",
  "companyIdentifier": "sample-company",
  "sourceRegion": "global",
  "title": "Software Engineer",
  "team": "Engineering",
  "location": "London",
  "allLocations": ["London"],
  "workplaceType": "hybrid",
  "commitment": "Full-time",
  "country": "GB",
  "postedAt": "2025-01-15T12:00:00.000Z",
  "descriptionText": "You will design and maintain dependable services.",
  "fullDescriptionText": "Join our team. You will design and maintain dependable services.",
  "hostedUrl": "https://jobs.lever.co/sample-company/123e4567-e89b-42d3-a456-426614174000",
  "applyUrl": "https://jobs.lever.co/sample-company/123e4567-e89b-42d3-a456-426614174000/apply",
  "observedAt": "2025-01-15T12:05:00.000Z"
}
```

HTML fields preserve employer-supplied markup. Treat that content as untrusted when rendering it in another application.

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

Pay-per-event pricing has a **$0.00005 start event** and charges once for each job saved. The per-job rate depends on your Apify plan:

| Plan | Price per saved job |
| --- | ---: |
| Free | $0.00184 |
| Bronze | $0.00160 |
| Silver | $0.001248 |
| Gold | $0.00096 |
| Platinum | $0.00096 |
| Diamond | $0.00096 |

Approximate Free-plan examples:

- 20 jobs: $0.03685
- 100 jobs: $0.18405
- 400 jobs: $0.73605

Only records written to the default dataset incur the job event. Filtered-out, duplicate, failed, and empty records are not job events. The Apify Console shows the authoritative charge for each run.

### Scheduling a Lever job postings monitor

Use an Apify Schedule to run the same input at a chosen interval. Send completed datasets to a webhook, Make, Zapier, Google Sheets, or your own database. Keep snapshots if you need history because the source itself represents the current board.

For large watchlists, group companies according to your downstream ownership and latency needs. `maxItems` is a global cap, and inputs are processed in order.

### Integrations and exports

The default dataset supports JSON, JSONL, CSV, Excel, XML, and RSS exports through Apify. Common patterns include:

- append each observation to a warehouse;
- upsert current records by company and job ID;
- notify only after a downstream comparison detects a new ID;
- index `fullDescriptionText` for search;
- join teams and locations to an employer research table.

### Run with the Apify API

Replace `YOUR_TOKEN` with an Apify API token.

#### cURL

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/automation-lab~lever-job-postings-monitor/runs?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"companyIdentifiers":["palantir"],"maxItems":20}'
```

To wait for results in one request:

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/automation-lab~lever-job-postings-monitor/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"companyIdentifiers":["palantir"],"maxItems":20}'
```

#### JavaScript

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/lever-job-postings-monitor').call({
    companyIdentifiers: ['palantir'],
    maxItems: 20,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient(token="YOUR_TOKEN")
run = client.actor("automation-lab/lever-job-postings-monitor").call(run_input={
    "companyIdentifiers": ["palantir"],
    "maxItems": 20,
})
items = client.dataset(run["defaultDatasetId"]).list_items().items
print(items)
```

### Use with Apify MCP

Add the Actor to Claude Code:

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com?tools=automation-lab/lever-job-postings-monitor"
```

#### Claude Desktop setup

Add this server object to the `mcpServers` section of Claude Desktop's configuration:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=automation-lab/lever-job-postings-monitor"
    }
  }
}
```

#### Cursor setup

Add the same `apify` HTTP server URL in **Cursor Settings → MCP**.

#### VS Code setup

Add the same `apify` HTTP server URL to your VS Code MCP server configuration. The endpoint exposes this Actor as an MCP tool after Apify authentication.

Example prompts:

- “Export the first 50 current Palantir jobs from Lever.”
- “Find Spotify engineering jobs in London and return their apply URLs.”
- “Run my Palantir and Jobgether Lever watchlist and group the results by team.”

MCP invokes the same input contract and returns links to the run and dataset.

### Reliability and failure behavior

Temporary timeouts, HTTP 429 responses, and temporary server errors receive up to three attempts with exponential backoff. Invalid identifiers, unsupported hosts, stable 4xx responses, non-JSON responses, and malformed source records fail rather than silently becoming an empty successful run.

A valid filter with no matches succeeds with an empty dataset. A removed direct job URL normally fails with a not-found error, allowing scheduled workflows to distinguish a missing target from a genuine empty filter result.

### Limits

- Only public `jobs.lever.co` and `jobs.eu.lever.co` boards are supported.
- The Actor does not discover which companies use Lever.
- Employer-provided fields vary and can be missing, inconsistent, or outdated.
- The source may change its interface, rate limits, or availability.
- Inputs are processed sequentially, so a low global limit may be filled by an earlier board.
- The Actor returns current public records; it does not independently store history or emit change events.
- HTML is preserved as supplied and should be sanitized according to your application's rendering policy.

### Responsible use and legality

Use public job data for legitimate purposes and follow applicable law, Lever's terms, employer terms, privacy requirements, and your organization's policies. Do not use the Actor to discriminate, harass, spam applicants or employers, or make automated high-impact employment decisions without appropriate review.

Descriptions may contain names, email addresses, or other public contact details chosen by an employer. Minimize collection, retention, and redistribution to what your purpose requires. This Actor is independent and is not affiliated with or endorsed by Lever.

### Troubleshooting

#### Why did my company identifier return 404?

Confirm the identifier from the first path segment of a current public Lever board URL. The employer may have migrated to another applicant-tracking system or renamed its site.

#### Why is my dataset empty?

Remove filters and try a small board export. If unfiltered records appear, add filters one at a time. Source labels are employer-controlled, so `onsite`, location names, and commitments may differ from your expected spelling.

#### Why did only the first company produce records?

`maxItems` is global and companies are processed in input order. Increase it or run companies separately when you require a quota from every board.

#### Does the Actor bypass private boards or authentication?

No. It supports anonymous public Lever postings only.

#### Can it alert me about new jobs?

Use an Apify Schedule and compare stable IDs between run datasets in a webhook or automation. The Actor supplies the current snapshots and observation timestamps; the downstream comparison owns alert state.

### FAQ

#### Does it support direct job URLs?

Yes. Paste a full public Lever posting URL in `startUrls`. The posting must still pass any supplied filters.

#### Does it include full descriptions?

Yes. It exports separate plain-text and original HTML sections plus a combined `fullDescriptionText` value.

#### Does it use a proxy or browser?

No automatic paid proxy or browser fallback is enabled. The public structured interface supplies the promised fields directly.

#### How are duplicate inputs handled?

Repeated targets are deduplicated before fetching, and result identity uses company identifier plus job ID.

#### Are closed jobs included?

The Actor exports records currently returned by Lever. A posting no longer returned by the current board will not appear in a later board snapshot.

### Related Actors

For a source-specific alternative using another applicant-tracking system, see [Rippling ATS Jobs Scraper](https://apify.com/automation-lab/rippling-ats-jobs-scraper). Use each source-specific Actor for the ATS that actually hosts the target employer's public board.

# Changelog

This Actor's version history is a separate document: https://apify.com/automation-lab/lever-job-postings-monitor/changelog.md

# Actor input Schema

## `companyIdentifiers` (type: `array`):

Lever site identifiers from jobs.lever.co URLs, for example palantir. You can combine identifiers with start URLs.

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

Public jobs.lever.co or jobs.eu.lever.co company boards and direct job posting URLs.

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

Maximum unique matching job records saved across all companies. Pagination stops after this many matching records are found.

## `titleQuery` (type: `string`):

Optional case-insensitive text that must appear in the public job title, for example engineer.

## `teams` (type: `array`):

Optional case-insensitive team or department fragments. A job is included when any value matches.

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

Optional case-insensitive location fragments, for example London, New York, or Remote.

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

Optional Lever workplace modes. Select one or more modes to keep.

## `commitments` (type: `array`):

Optional case-insensitive employment commitment fragments, for example Full-time or Internship.

## `postedAfter` (type: `string`):

Optional ISO 8601 date or timestamp. Only postings created at or after this instant are included.

## Actor input object example

```json
{
  "companyIdentifiers": [
    "palantir"
  ],
  "startUrls": [
    {
      "url": "https://jobs.lever.co/palantir"
    }
  ],
  "maxItems": 20
}
```

# Actor output Schema

## `overview` (type: `string`):

Current Lever job records with recruiting fields, descriptions, source URLs, and observation timestamps.

# 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 = {
    "companyIdentifiers": [
        "palantir"
    ],
    "startUrls": [
        {
            "url": "https://jobs.lever.co/palantir"
        }
    ],
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/lever-job-postings-monitor").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 = {
    "companyIdentifiers": ["palantir"],
    "startUrls": [{ "url": "https://jobs.lever.co/palantir" }],
    "maxItems": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/lever-job-postings-monitor").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 '{
  "companyIdentifiers": [
    "palantir"
  ],
  "startUrls": [
    {
      "url": "https://jobs.lever.co/palantir"
    }
  ],
  "maxItems": 20
}' |
apify call automation-lab/lever-job-postings-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/lever-job-postings-monitor"
        }
    }
}
```

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/UNJPHn5f7xFP9QTRx/builds/Fkpqp4edHMrd3S6PX/openapi.json
