# Career Page Jobs Scraper: Greenhouse, Lever, Ashby, Workable (`antsonahill/career-page-jobs-scraper`) Actor

Get open jobs straight from company career pages. Reads the documented public job-board APIs of Greenhouse, Lever, Ashby, Workable, Teamtailor and Gem and returns every posting in one schema. Filter by title, location or date, or get only jobs that are new since the last run.

- **URL**: https://apify.com/antsonahill/career-page-jobs-scraper.md
- **Developed by:** [Anton Sajin](https://apify.com/antsonahill) (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 job returneds

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 Scraper

Get every open job from company career pages, in one clean schema, straight from the source.

Most companies publish their jobs through an applicant tracking system (ATS) such as Greenhouse, Lever or Ashby. This Actor reads the public job-board API of six of those systems, so you get the listing the company itself maintains: complete, current, and free of the duplicates and stale posts that job aggregators carry.

Give it a list of companies. Get back titles, locations, departments, salary ranges, posting dates, apply links and full descriptions.

### What you can do with it

- **Track hiring at companies you care about.** See who is hiring for what, and when a new role opens.
- **Monitor competitors.** See which teams they are growing and where.
- **Get alerts for new jobs.** Turn on *Only jobs that are new since the last run*, schedule the Actor daily, and receive only fresh postings.
- **Research the job market.** Titles, locations, pay ranges and descriptions in a consistent format, ready for analysis.

### Supported systems

| ATS | Example input | Notes |
| --- | --- | --- |
| Greenhouse | `https://boards.greenhouse.io/discord` | Pay ranges when the company publishes them |
| Lever | `https://jobs.lever.co/palantir` | US and EU boards. Read at one request per second, as Lever asks |
| Ashby | `https://jobs.ashbyhq.com/ramp` | Salary ranges included |
| Workable | `https://apply.workable.com/huggingface` | |
| Teamtailor | `https://career.teamtailor.com` | Boards on a `teamtailor.com` address |
| Gem | `https://jobs.gem.com/gem` | |

A system is supported only when its vendor documents the endpoint as public and unauthenticated, its robots.txt allows the path, and nothing in its published terms restricts reading it. Workday, SmartRecruiters, Rippling, Recruitee, Personio, Pinpoint and Breezy HR are deliberately not supported, because their terms or robots.txt rule this kind of access out or leave it unclear.

### How to tell it which companies

Put one company per line in **Companies**. Four forms work:

1. **The job board URL**, as in the table above. Most reliable.
2. **The company's own careers page**, for example `https://example.com/careers`. The Actor fetches that one page, if the site's robots.txt allows it, and looks for a link to or an embed of a supported job board. Pages that load their jobs with JavaScript, or from an unsupported system, cannot be read this way.
3. **Shorthand** `ats:slug`, for example `greenhouse:discord` or `lever:palantir`.
4. **Just the name**, for example `huggingface`. The Actor tries the name on each system in the order of the table and uses the first board that has open jobs. Check `companyName` in the results, since two companies can share a short name; use the shorthand when you know the system.

Inputs that cannot be read never fail the run. They are listed with the reason in the `SUMMARY` record of the run's key-value store.

### Input example

```json
{
    "companies": [
        "https://boards.greenhouse.io/discord",
        "https://jobs.ashbyhq.com/ramp",
        "lever:palantir",
        "huggingface"
    ],
    "titleKeywords": ["engineer", "developer"],
    "excludeTitleKeywords": ["intern"],
    "locations": ["remote", "new york"],
    "postedWithinDays": 14,
    "includeDescription": true
}
```

#### Filters

All filters are optional, and a job must pass every filter you set. Matching is case-insensitive and looks for the text anywhere in the field.

| Field | Keeps jobs where |
| --- | --- |
| `titleKeywords` | the title contains at least one of the phrases |
| `excludeTitleKeywords` | the title contains none of the phrases |
| `locations` | a location or the country contains at least one of the phrases |
| `departments` | the department or team contains at least one of the phrases |
| `employmentTypes` | the type is one of `full-time`, `part-time`, `contract`, `internship`, `temporary`, `other` |
| `remoteOnly` | the job is marked remote |
| `postedWithinDays` | the job was published in the last N days |

Two things to know. A job with no published date is dropped when `postedWithinDays` is set. A job with no stated employment type is dropped when `employmentTypes` is set; Greenhouse and Teamtailor often do not state one.

#### Monitoring new jobs

Set `onlyNewJobs` to `true` and schedule the Actor. Each run returns only jobs it has not returned before, and you are charged only for those. Use `monitorId` to keep separate feeds, for example one per saved task.

If a limit such as `maxJobsPerCompany` holds some new jobs back, they are returned on the next run rather than lost.

### Output

One item per job:

```json
{
    "id": "ashby:ramp:34413f8d-26bf-4bbc-8ade-eb309a0e2245",
    "ats": "ashby",
    "company": "ramp",
    "companyName": null,
    "title": "Security Engineer, Cloud",
    "department": "Engineering",
    "team": "Backend",
    "location": "New York, NY (HQ)",
    "locations": ["New York, NY (HQ)", "Remote (Canada)", "Remote (US)", "Miami, FL"],
    "country": "USA",
    "workplaceType": "hybrid",
    "isRemote": true,
    "employmentType": "full-time",
    "salary": {
        "min": 211400,
        "max": 290600,
        "currency": "USD",
        "interval": "year",
        "text": "$211.4K – $290.6K • Offers Equity"
    },
    "postedAt": "2026-04-07T17:12:35.753Z",
    "updatedAt": null,
    "url": "https://jobs.ashbyhq.com/ramp/34413f8d-26bf-4bbc-8ade-eb309a0e2245",
    "applyUrl": "https://jobs.ashbyhq.com/ramp/34413f8d-26bf-4bbc-8ade-eb309a0e2245/application",
    "requisitionId": null,
    "descriptionHtml": "<h1>About Ramp</h1>…",
    "descriptionText": "ABOUT RAMP\n\nRamp is building…",
    "scrapedAt": "2026-10-11T00:02:51.120Z"
}
```

Fields the ATS does not publish are `null` rather than guessed. For example, Lever, Ashby and Gem do not include the company's display name, and salary is present only where the company chose to publish it.

`id` is stable between runs, so you can use it as a key when you sync jobs into a database or spreadsheet.

### Pricing

Pay per result: **$1.50 per 1,000 jobs** returned. There is no monthly fee, and you pay nothing for companies that return no jobs or for jobs your filters remove.

With `onlyNewJobs` on, a daily run over hundreds of companies typically costs a few cents, because only the new postings count.

You can cap what a run may spend with the *Maximum cost per run* setting; the Actor stops cleanly when it reaches your limit.

### Using the data responsibly

- **Job descriptions belong to the employers who wrote them.** Reading and analysing them is one thing; republishing them wholesale is another. If you show jobs to other people, link to the original posting (`url`) and check that your use is one the employer would expect.
- **No AI training on Workable and Teamtailor content.** Both signal in their robots.txt that their content may be used for search and as AI input but not to train AI models. Respect that for jobs with `ats` set to `workable` or `teamtailor`.
- **The Actor collects job postings only.** It never logs in anywhere and collects no applicant or personal data.

### FAQ

**Where does the data come from?** From endpoints each vendor documents as public and unauthenticated: the Greenhouse Job Board API, the Lever Postings API, Ashby's public job posting API, Workable's public jobs endpoint, the Teamtailor career-site RSS feed and the Gem Job Board API. The Actor identifies itself in its User-Agent and follows the request limits those vendors publish. This is a description of how the tool works, not legal advice about your use of the data.

**How fresh is the data?** Every run reads the live boards. Nothing is cached between runs.

**How fast is it?** Each system returns a company's whole board in one or two requests, so dozens of companies finish in seconds. Lever boards are read one per second at Lever's request, so a list of several hundred Lever companies takes a few minutes.

**A company I need is not supported.** Many large employers use systems this Actor leaves out on purpose (see *Supported systems*). If a company uses a system that documents a public job feed and is missing here, open an issue on the Issues tab with the careers page URL.

**Something looks wrong.** Open an issue on the Issues tab and include the run ID.

# Actor input Schema

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

One company per line. Use a job board URL (https://boards.greenhouse.io/discord, https://jobs.lever.co/palantir, https://jobs.ashbyhq.com/ramp), the company's own careers page if it links to a supported job board (https://example.com/careers), the shorthand ats:slug (greenhouse:discord), or just a company name (huggingface) to try each supported system in turn. Supported: Greenhouse, Lever, Ashby, Workable, Teamtailor, Gem.

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

Keep only jobs whose title contains at least one of these words or phrases. Not case sensitive. Leave empty to keep every title.

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

Drop jobs whose title contains any of these words or phrases, for example intern or senior.

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

Keep only jobs whose location or country contains at least one of these, for example London, Germany or Remote.

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

Keep only jobs whose department or team contains at least one of these, for example Engineering or Sales.

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

Keep only these employment types. Jobs whose type the company did not state are dropped when this is set.

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

Keep only jobs marked as remote by the company or with Remote in the location.

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

Keep only jobs published in the last N days. A job with no published date is dropped when this is set. Leave empty for no date filter.

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

Return the full description as HTML and plain text. Turn off for faster, smaller results when you only need titles, locations and links.

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

Stop after this many jobs for each company. Leave empty for all jobs.

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

Stop the run after this many jobs across all companies. Leave empty for no limit.

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

Remember which jobs were already returned and return only new ones on later runs. Schedule the Actor daily to get a feed of new postings, and pay only for the new ones.

## `monitorId` (type: `string`):

Only used with "Only jobs that are new". Runs with the same monitor name share one memory of seen jobs. Use different names to keep separate feeds, for example one per saved task.

## Actor input object example

```json
{
  "companies": [
    "https://boards.greenhouse.io/discord",
    "https://jobs.lever.co/palantir",
    "https://jobs.ashbyhq.com/ramp",
    "huggingface"
  ],
  "remoteOnly": false,
  "includeDescription": true,
  "maxJobsPerCompany": 10,
  "onlyNewJobs": false
}
```

# Actor output Schema

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

One item per job posting.

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

Per-company status: jobs found, jobs returned, and why any input could not be read.

# 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/discord",
        "https://jobs.lever.co/palantir",
        "https://jobs.ashbyhq.com/ramp",
        "huggingface"
    ],
    "maxJobsPerCompany": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("antsonahill/career-page-jobs-scraper").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/discord",
        "https://jobs.lever.co/palantir",
        "https://jobs.ashbyhq.com/ramp",
        "huggingface",
    ],
    "maxJobsPerCompany": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("antsonahill/career-page-jobs-scraper").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/discord",
    "https://jobs.lever.co/palantir",
    "https://jobs.ashbyhq.com/ramp",
    "huggingface"
  ],
  "maxJobsPerCompany": 10
}' |
apify call antsonahill/career-page-jobs-scraper --silent --output-dataset

```

## MCP server setup

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

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/56nBmAilzAhhcxc8I/builds/onFyyQ6TCqI3fZyin/openapi.json
