# Career Page Jobs: Greenhouse, Lever & Ashby (`yzy001/career-page-jobs`) Actor

All open jobs from company career pages on Greenhouse, Lever and Ashby in one table: title, team, locations, remote/hybrid/on-site, salary, visa sponsorship, tech tags. Optional tracking reports new and closed jobs since your last run.

- **URL**: https://apify.com/yzy001/career-page-jobs.md
- **Developed by:** [宗諭 楊](https://apify.com/yzy001) (community)
- **Categories:** Jobs, Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 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 Page Jobs: Greenhouse, Lever & Ashby

Give it company career pages hosted on Greenhouse, Lever or Ashby and get every open job back in one table with the same columns, whichever system the company uses. Turn on tracking and each run also tells you which jobs are new and which have closed since your last run.

### What you get for each job

| Field | Meaning |
|---|---|
| `company`, `ats`, `board` | Company name (as the board gives it, or the board name), which system it is on, and the board name |
| `title`, `department`, `team`, `locations` | As listed on the career page |
| `workplace` | `REMOTE`, `HYBRID`, `ONSITE`, `MIXED` (remote offered as an alternative to an office) or `UNKNOWN` |
| `workplaceSource` | `ats` when the system has its own field for it (Lever, Ashby), `text` when it was read from the title, location and description |
| `remoteAllowed` | `true` when the job can be done remotely, `false` when on-site or hybrid, empty when not stated |
| `salary` | `min`, `max`, `currency`, `period` (`YEAR`, `MONTH`, `HOUR`), `source` (`ats` or `text`) |
| `visaSponsorship` | `true` / `false` when the description says so, empty when it does not mention visas |
| `employmentTypes`, `techTags` | Full-time, contract and so on; technologies named in the description |
| `postedAt`, `updatedAt`, `jobUrl`, `applyUrl`, `jobId` | Dates and links |
| `description` | The full text, when `includeDescription` is on |
| `changeStatus`, `firstSeen` | With tracking: `FIRST_RUN`, `NEW` or `SEEN`, and when this job was first seen |

With tracking on, a job that disappeared since the previous tracked run comes back once as a row with `recordType` `CLOSED`, its title, locations and the date it was first seen.

### How accurate the read-from-text fields are

Lever and Ashby give a workplace type, and Ashby gives salary ranges; those are passed through as the company entered them. Greenhouse gives neither, so they are read from the text, and that was measured on Greenhouse jobs labelled by hand before the Actor was run on them:

| Field | 30 jobs from 5 companies (first version) | 20 jobs from 4 other companies (after fixing what the first set showed) |
|---|---|---|
| workplace | 70.0% | 90.0% |
| remoteAllowed | 86.7% | 95.0% |
| visaSponsorship | 100% | 75.0% |
| salary | 93.3% | 100% |

The visa misses in the second set were all one phrase ("without sponsorship for an export license", which is about export control, not visas) and have been fixed, as have the two workplace misses. Fields that the text does not state stay empty instead of guessed.

### Filters

`keywords` (title or description), `locations`, `departments`, `workplace`, `remoteAllowedOnly`, `minAnnualSalary` with `salaryCurrency`, and `maxItems`. Tracking always compares whole boards, so filters never make a job look closed.

### Run summary

Stored under `SUMMARY` in the key-value store: each board's status (`OK`, `NOT_FOUND`, or the error), jobs read and kept, new and closed counts, the workplace mix, how many salaries came from the system and how many from text, median salary midpoints per currency, top tech tags and the caveats.

### Things to know

- **Board names come from the career page URL.** jobs.lever.co/spotify is `spotify`. A name that does not exist is reported as `NOT_FOUND` for that board; if none of them exist, the run is refused and says so.
- **Salary is what the posting states.** Ranges across zones or roles are reported lowest to highest. Nothing is converted between currencies. Funding, market sizes and 401k are not pay.
- **Visa sponsorship is usually not mentioned**, so the field is usually empty.
- **Tracking state lives in your own account**, in a key-value store named `career-page-jobs-state`. Delete it to start over.
- Other applicant-tracking systems (Workday, SmartRecruiters and so on) are not supported.

### Data

The Actor reads the same public job-board APIs that the companies' own career pages use. It does not log in or work around any blocking, and it waits a second between Lever requests as Lever asks.

# Actor input Schema

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

Career page URLs on Greenhouse (boards.greenhouse.io/<name>), Lever (jobs.lever.co/<name>) or Ashby (jobs.ashbyhq.com/<name>), or greenhouse:<name>, lever:<name>, ashby:<name>. Up to 50.

## `trackChanges` (type: `boolean`):

Compare with your previous tracked run of the same boards: each job is marked NEW or SEEN, and jobs that disappeared come back once as CLOSED rows. The comparison is kept in a key-value store named career-page-jobs-state in your account.

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

Optional. Keep jobs whose title or description contains any of these words.

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

Optional. Keep jobs whose location contains any of these, for example London, Remote, Germany.

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

Optional. Keep jobs whose department or team contains any of these, for example Engineering, Sales.

## `workplace` (type: `array`):

Optional. Keep only these workplace types.

## `remoteAllowedOnly` (type: `boolean`):

Keep only jobs that can be done remotely (REMOTE or MIXED).

## `minAnnualSalary` (type: `integer`):

Optional. Keep only jobs whose stated annual pay reaches this amount in the currency below; jobs without a stated annual salary in that currency are left out. 0 = no filter.

## `salaryCurrency` (type: `string`):

Currency for the minimum salary filter. Nothing is converted.

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

Include the full description text. Off by default to keep rows small.

## `allowPartial` (type: `boolean`):

On by default: if one board cannot be read, the others are still returned and the status message names it. Turn off to make any failed board fail the run.

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

Upper bound on rows returned.

## Actor input object example

```json
{
  "companies": [
    "https://boards.greenhouse.io/figma",
    "https://jobs.lever.co/spotify",
    "https://jobs.ashbyhq.com/linear"
  ],
  "trackChanges": false,
  "keywords": [],
  "locations": [],
  "departments": [],
  "workplace": [],
  "remoteAllowedOnly": false,
  "minAnnualSalary": 0,
  "salaryCurrency": "USD",
  "includeDescription": false,
  "allowPartial": true,
  "maxItems": 5000
}
```

# Actor output Schema

## `items` (type: `string`):

Normalised jobs from every board.

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

Per-board status and counts, workplace and salary statistics, caveats.

# 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": [
        "https://boards.greenhouse.io/figma",
        "https://jobs.lever.co/spotify",
        "https://jobs.ashbyhq.com/linear"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("yzy001/career-page-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 = { "companies": [
        "https://boards.greenhouse.io/figma",
        "https://jobs.lever.co/spotify",
        "https://jobs.ashbyhq.com/linear",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("yzy001/career-page-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 '{
  "companies": [
    "https://boards.greenhouse.io/figma",
    "https://jobs.lever.co/spotify",
    "https://jobs.ashbyhq.com/linear"
  ]
}' |
apify call yzy001/career-page-jobs --silent --output-dataset

```

## MCP server setup

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