# ATS Jobs API: Greenhouse & Lever Jobs, Ashby & Career Pages (`conserving_celerytop/live-career-page-jobs-api`) Actor

$0.01 per company, up to 1,000 jobs. Every open job on company career pages, read live from Greenhouse, Lever, Ashby, Workday, Eightfold, Workable, Personio, Teamtailor, Recruitee, JOIN, Gem, Freshteam and 10 more. New-job alerts to Slack on a schedule. No API key or login.

- **URL**: https://apify.com/conserving\_celerytop/live-career-page-jobs-api.md
- **Developed by:** [Don Mangu](https://apify.com/conserving_celerytop) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $8.00 / 1,000 companies

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

**Get every open job on the career pages of the companies you name, read live when you run it.** So you get the complete list, including jobs posted months ago that are still open. Paste job board links, websites or company names. For each company you get one row per open job, with title, department, location, remote or hybrid, salary when published, posted date and apply link. Use it as a **Greenhouse jobs API**, a **Lever** and **Ashby** jobs API, or a **career page scraper** for 22 job boards (ATS).

**$0.01 per company, up to 1,000 jobs included.** No API key, proxy or login. A company with 100 open jobs costs $0.01, so if your companies have about 100 open jobs each, you pay about $0.10 per 1,000 jobs. The Store's "from $8.00 / 1,000 companies" is $0.008 per company on Apify's Business plan. Apify's free plan gives $5 of credit a month, which covers about 500 companies.

- **Job sites and newsletters** pull current jobs from a list of employers. On a daily schedule, **Only new jobs** returns just the new and closed ones, to add and expire listings.
- **Recruiters** watch target companies for new roles, with salaries when published.
- **Sales and RevOps teams** track **hiring signals**. One summary row per company gives open jobs, jobs posted in the last 7 and 30 days, and the share of engineering and sales roles.
- **Developers and AI agents** call it through the Apify API.

It reads Greenhouse, Lever, Ashby, Workable, Personio, Teamtailor, Recruitee, JOIN, Homerun, Gem, JazzHR, Paylocity, Freshteam, PageUp, Polymer, HireHive, HiringThing, Trakstar Hire, ClearCompany and GoHire, and Workday and Eightfold where the employer's robots.txt allows it. A plain company name works for many companies but not all, and a website such as `stripe.com` works when the site links to its job board. A company with nothing to return gets a status row that says why.

It does not search all companies by keyword. You bring the list, or pick a ready-made one, and it returns what their job boards show right now. See [how this differs from a job search database](#how-is-this-different-from-a-job-search-database).

**Try it now.** Click **Try for free** and create a free Apify account, no credit card needed. The form is filled in with Stripe (Greenhouse), Palantir (Lever) and Ashby (Ashby). Click **Start** to get about 1,000 jobs for $0.03 in a few seconds. To try more companies next, pick a list in **Ready-made company lists**, such as Remote-first companies (90 companies, about $0.90).

### Get new jobs every morning

1. Turn on **Only new jobs since my last check** and paste a Slack incoming webhook into **Alert webhook URL**.
2. Click **Start** once. This first check returns every open job, costs $0.01 per company and sends no message.
3. Click **Save as a new task**, then add the task to a daily schedule in **Schedules** and enable it.

After that, each check sends one message when jobs are new or closed, and nothing on quiet days. A later check costs $0.002 per started 1,000 open jobs on a company's board, so 50 companies with up to 1,000 open jobs each cost $0.10 a day, about $3 a month. [All steps, and Google Sheets or email instead](#get-only-new-and-closed-jobs-on-a-schedule).

### What job data do you get?

These are four of the 1,080 rows that the prefilled input returned on September 24, 2026.

| Company | Title | Location | Workplace | Job function | Salary | Posted |
|---|---|---|---|---|---|---|
| Stripe | Sourcer, GTM | Chicago, Atlanta, US-Remote | remote | people\_hr | | 2026-09-24 |
| Palantir | Security Systems Engineer | Seattle, WA | hybrid | it\_security | | 2026-09-23 |
| Ashby | Product Manager, Onboarding and Growth | Remote - US | remote | product | 180,000 to 260,000 USD a year | 2026-09-11 |
| Ashby | Product Support Engineer - EMEA | United Kingdom | remote | customer\_success | 75,000 to 104,000 GBP a year | 2026-09-14 |

Each row also has the job link, country code, seniority, job type and yearly salary. Empty cells are values the board does not give. Turn on **Include job description** to add the full text, the tools each job names, and the pay, years of experience, education and visa sponsorship written in it. In our tests, 96.5% of job locations got a country.

### How to scrape jobs from Greenhouse, Lever, Ashby and other career pages

1. Click **Try for free**.
2. In **Companies**, paste job board links, websites or company names, one per line, up to 500. For a board link, open any job on the company's careers page and copy its link.
3. Set filters if you need them, such as **Title includes** or **Location**.
4. Click **Start**. Open the **Jobs** view and export as JSON, CSV or Excel.

For descriptions from boards with thousands of jobs, choose 512 MB of memory.

### How much does it cost?

You pay $0.01 per company, less on paid Apify plans, and that includes up to 1,000 of its open jobs.

| Event | Price | What you get |
|---|---|---|
| Company (`company-lookup`) | $0.01 per company; $0.0095 on Starter, $0.009 on Scale, $0.008 on Business | All open jobs of one company, up to 1,000 rows, including the first check with **Only new jobs**, also when its board is empty, not found or fails |
| Extra job block (`extra-1000-jobs`) | $0.01 per 1,000 jobs | Each further 1,000 rows of the same company, or part of them |
| Repeat monitor check (`new-jobs-check`) | $0.002 per check | A later check with **Only new jobs**, per 1,000 open jobs on the board or part of them, also when the board is empty, not found or fails |
| Description block (`job-details`) | $0.01 per 200 jobs | Descriptions of 200 jobs, or part of them, on JazzHR, Paylocity, Freshteam, JOIN, Polymer, ClearCompany, GoHire, Workday or Eightfold, and on HiringThing career portals that list the jobs of several locations. Other boards include descriptions at no extra cost |

Invalid, unsupported, duplicate and skipped entries, and websites we cannot open, are free. [Statuses and charges](#statuses-and-charges) lists every case.

- A company with 2,624 jobs costs $0.03, the lookup plus 2 extra blocks.
- Filters and **Max jobs per company** lower the price of job rows only above 1,000 jobs, and they can lower the description price at any size. Filters that read descriptions add the description price on the boards above.
- If your spending limit cannot cover a company's charges, we deliver none of its jobs and charge nothing for it.
- In a normal run, Apify platform usage is included.

| Example | This Actor | Per 1,000 jobs | Per-job pricing, $1 to $4 per 1,000 jobs |
|---|---|---|---|
| 1 company with about 690 open jobs | $0.01 | $0.014 | $0.69 to $2.76 |
| 100 companies with about 40 jobs each | $1.00 | $0.25 | $4 to $16 |
| 85 companies of mixed size, 7,096 jobs | $0.86 | $0.12 | $7.10 to $28.38 |

Per-job pricing is cheaper only for boards with fewer than 10 jobs, and for daily checks of small companies that rarely post.

### What can you use it for?

#### Hiring signals for an account list

Paste your accounts as websites or domains. Set **Rows to return** to one summary row per company. Each row gives open roles and roles posted in the last 30 days, in total and per job function. It lists open director, VP and C-level roles, and roles whose title calls them a first or founding hire. With **Only new jobs** on a weekly schedule, it also names the job functions and countries where a company started hiring since your last check.

Turn on **Include job description**, free on most boards, to add the tools the posts name, such as Salesforce or Snowflake, and first hires that only the job text mentions.

#### Spot likely ghost jobs

A job that stays posted for months, or comes back under a new id, may not be a real opening. Set **Posted before** to `90 days` to list jobs posted more than 90 days ago, or read `jobsOpenOver90Days` in the summary row. With **Only new jobs**, new jobs with the title and location of a job closed in the last 30 days have `reposted` set to true, closed jobs have `daysOpen`, and the summary row adds `repostedJobs` and `medianDaysOpen`. An old or reposted job is a sign, not proof, since some companies hire for the same role all year.

#### Monitor competitor job postings

Follow a few competitors weekly and see which teams, locations and seniority levels they hire for.

#### Salary ranges from job posts

Where a company publishes pay, you get minimum, maximum, currency and period, plus a yearly figure for comparison. Set a minimum yearly salary to keep only jobs at or above it.

### Get only new and closed jobs on a schedule

1. Turn on **Only new jobs since my last check** and give the watchlist a **Monitor name**.
2. Click **Start** once. This first check returns every open job and costs $0.01 per company.
3. Click **Save as a new task**.
4. Open **Schedules**, click **Create new**, choose how often and your time zone, add the task and click **Enable**. New schedules start disabled.

Each later check returns new jobs that match your filters and closed jobs you received, with `change` set to `new` or `closed`. It costs $0.002 per company with up to 1,000 open jobs.

**Also return updated jobs** (`includeUpdatedJobs`) adds jobs whose title, location, salary or job type changed, with `change` set to `updated`. With **Include job description**, only new jobs get a description.

What you received is saved in your Apify account, in the key-value store `live-career-page-jobs-api-monitor-<monitor name>`. The status message starts with what changed, such as "12 new and 3 closed jobs at 5 of 40 companies".

#### Send new jobs to Slack, Google Sheets or email

**New jobs in Slack every morning.** Schedule the task for the morning. On its **Integrations** tab, choose Slack, connect your workspace, pick a channel and the events Run succeeded and Run failed, and paste this message. It comes after every check, also on quiet days.

```
*Job changes at your watchlist*
{{resource.statusMessage}}
<https://console.apify.com/storage/datasets/{{resource.defaultDatasetId}}|Open the jobs>
```

**Or skip quiet days with the built-in alert.** Paste a Slack, Discord or Teams incoming webhook, or a Make, Zapier or n8n webhook, into **Alert webhook URL** (`alertWebhookUrl`). After a check with new or closed jobs, it sends one free message with up to `alertMaxJobs` jobs (default 10) and their links, then links the rest. It sends nothing on quiet days, or on a first check unless you turn on `alertOnFirstCheck`.

**A Google Sheet of every new job.** On the same tab, add the [Google Sheets Import & Export](https://apify.com/lukaskrivka/google-sheets) Actor, connect Google, pick the spreadsheet, set Mode to `append`, and paste this Transform function. Each check adds a row per new or closed job.

```js
({ spreadsheetData, datasetData }) => spreadsheetData.concat(datasetData
    .filter((r) => r.rowType === 'job')
    .map((r) => ({
        change: r.change ?? '', company: r.companyName ?? r.companySlug ?? '', title: r.title ?? '', jobFunction: r.jobFunction ?? '',
        seniority: r.seniority ?? '', location: r.location ?? '', salaryAnnualMin: r.salaryAnnualMin ?? '',
        salaryAnnualMax: r.salaryAnnualMax ?? '', salaryCurrency: r.salaryCurrency ?? '', postedAt: r.postedAt ?? '', url: r.url ?? '',
    })))
```

Each new or closed job also has `alertText`, one plain line such as "New at Stripe: Senior Data Engineer, Remote (US), USD 180,000 to 240,000 a year", ready to map in Zapier, Make, n8n or Slack. The **Alerts** view shows just the fields an alert needs. In the API, add `view=alerts`, also with `format=csv`, `html` or `rss`.

**An email only on days with new jobs.** Paste a Make, Zapier or n8n webhook into **Alert webhook URL**, add a step that continues only when `newJobs` is above 0, and email the message's `text`. Apify's **Send results email via Gmail** integration, with the dataset attached as CSV, sends one after every check instead.

Three things to know before the first alert.

- Run the task once by hand before you add an integration, since the first check returns every open job as new.
- New filters or a new monitor name start a new memory, so the next check is a first check again, at $0.01 per company.
- A run by hand between scheduled runs takes the new jobs, so the next scheduled message will not show them. Tasks with the same monitor name and filters share one memory.

### Ready recipes

Paste a recipe into the JSON view of the form, put in your companies, and schedule it as above. Monthly costs assume up to 1,000 open jobs per company, after the first check.

**New sales jobs at your target accounts.** Every Monday to Slack, $0.009 per company a month.

```json
{"companies": ["https://boards.greenhouse.io/datadog", "https://boards.greenhouse.io/gongio", "https://jobs.ashbyhq.com/ramp"], "jobFunctions": ["sales"], "onlyNewJobs": true, "monitorName": "sales-accounts"}
```

**New jobs at AI companies.** Every day to Google Sheets, $0.06 per company a month.

```json
{"companies": ["https://jobs.ashbyhq.com/openai", "https://boards.greenhouse.io/anthropic", "https://jobs.ashbyhq.com/cursor"], "onlyNewJobs": true, "monitorName": "ai-companies-daily", "maxJobsPerCompany": 100}
```

**Competitor hiring by job function.** Every Monday by email, $0.009 per company a month. One summary row per competitor.

```json
{"companies": ["https://jobs.ashbyhq.com/cognition", "https://jobs.ashbyhq.com/replit", "https://jobs.ashbyhq.com/lovable"], "outputMode": "companies", "onlyNewJobs": true, "monitorName": "competitors-weekly"}
```

**Salary ranges by role at competitors.** Every Monday to Google Sheets, $0.009 per company a month.

```json
{"companies": ["https://jobs.ashbyhq.com/baseten", "https://jobs.ashbyhq.com/modal", "https://boards.greenhouse.io/coreweave"], "hasSalary": true, "onlyNewJobs": true, "includeUpdatedJobs": true, "monitorName": "competitor-salaries"}
```

### Supported job boards

| Job board | Paste a link like |
|---|---|
| Greenhouse | `boards.greenhouse.io/stripe`, `job-boards.greenhouse.io/stripe` |
| Lever | `jobs.lever.co/palantir`, `jobs.eu.lever.co/<company>` |
| Ashby | `jobs.ashbyhq.com/openai` |
| Workday | `nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite`, `wd1.myworkdaysite.com/recruiting/wf/WellsFargoJobs` |
| Eightfold | `paypal.eightfold.ai/careers`, `careers.micron.com/careers` |
| Workable | `apply.workable.com/blueground` |
| Personio | `ottonova.jobs.personio.com` (or `.de`) |
| Teamtailor | `polestar.teamtailor.com`, `<company>.na.teamtailor.com` |
| Recruitee | `fastned.recruitee.com` |
| JOIN | `join.com/companies/getklar` |
| Homerun | `moneybird.homerun.co` |
| Gem | `jobs.gem.com/retool` |
| JazzHR | `nro.applytojob.com` |
| Paylocity | `recruiting.paylocity.com/Recruiting/Jobs/All/<company-GUID>` |
| Freshteam | `upswing.freshteam.com/jobs` |
| PageUp | `careers.pageuppeople.com/865/cw/en-us/listing` |
| Polymer | `jobs.polymer.co/magnendo` |
| HireHive | `focus-ireland.hirehive.com` |
| HiringThing | `vault-manufacturing.hiringthing.com`, `<company>.applicant-tracking.com` |
| Trakstar Hire (Recruiterbox) | `stax.hire.trakstar.com` |
| ClearCompany | `medcor.hrmdirect.com/employment/job-openings.php` |
| GoHire | `jobs.gohire.io/dexerto-de5jlhjo` |

Workday and Eightfold sites are read only where the employer's robots.txt allows it. Greenhouse boards hosted in the EU are not supported yet. iCIMS, SmartRecruiters, Dayforce, Paycor, UKG, CareerPlug, Hireology, isolved, Avature, Pinpoint, Comeet, LinkedIn, Indeed and other boards whose terms do not clearly allow this use are not read. Neither are employers that list their jobs only on their own career site, such as Google, Apple, Amazon, Meta and Tesla. All of these return `unsupported_job_board`, free, and `error` says why.

In our test of 81 well-known company names, a plain name such as `stripe` or `Zalando SE` found the right company's board for 49. A name that is not found costs $0.01, and so does a website on which we find no job board (`no_job_board_found`), so for large employers paste a job's link instead. `matchedBy` says how each board was matched (`link`, `website`, `directory`, `name` or `name variant`). `warning` flags many names that may belong to another company, but not every one, so check `companyName` and `boardUrl` when it matters. When a company has several career sites, `warning` names the one we read.

#### Greenhouse job board API

Paste `boards.greenhouse.io/stripe` or any job link from the board to get every open job with department, location, posted date and published pay ranges. Descriptions are free.

#### Lever postings API

Paste `jobs.lever.co/palantir`, or `jobs.eu.lever.co/<company>` for a board in the EU, to get every open job with department, location, remote or hybrid, job type, posted date and salary range when shown. Descriptions are free.

#### Ashby jobs API

Paste `jobs.ashbyhq.com/openai` to get every open job with department, location, remote or hybrid, job type, posted date and pay when published. Descriptions are free.

#### Workday jobs API

Paste a career site link, such as `nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite`, to get every open job with location, department and apply link. Descriptions cost $0.01 per 200 jobs.

#### Eightfold career sites

Paste a career site link, such as `paypal.eightfold.ai/careers` or `careers.micron.com/careers`, to get every open job with location, remote or hybrid, posted date and apply link. Descriptions cost $0.01 per 200 jobs.

### For AI agents

Use it to answer "is company X hiring for Y right now?" or to list every open job at the companies you name. Only `companies` is needed, up to 500 per run. It reads the 22 job boards in [Supported job boards](#supported-job-boards), and a link to one of them works best.

```json
{"companies": ["stripe", "linear.app", "https://jobs.lever.co/palantir"], "titleIncludes": ["data engineer"], "postedSince": "30 days", "maxJobsPerCompany": 20}
```

Each job row has `company`, `companyStatus`, `title`, `department`, `location`, `countryCode`, `jobFunction`, `salaryAnnualMin`, `salaryAnnualMax`, `postedAt` and `url`. A company with no job to return gets one status row. `no_matching_jobs` means it has open jobs but none match, `no_open_jobs` means its board is empty, and `not_found` means we found no board under that name or link.

This Actor has no MCP endpoint of its own. Agents signed in to Apify's MCP server can find and run it like any other Actor, as a normal run at the prices above. To add just this Actor as a tool in Claude, ChatGPT, Cursor or another MCP client, connect `https://mcp.apify.com/?tools=fetch-actor-details,conserving_celerytop/live-career-page-jobs-api` and sign in to Apify.

For GPT Actions, LangChain or another OpenAPI tool, the run endpoints are described at `https://api.apify.com/v2/acts/conserving_celerytop~live-career-page-jobs-api/builds/default/openapi.json`. The file asks for your Apify token in the `token` parameter. An `Authorization: Bearer` header also works.

### Apify API and integrations

The **API** tab has ready code for JavaScript, Python and other clients. This request starts a run, waits up to 300 seconds and returns one row per job.

```bash
curl -X POST "https://api.apify.com/v2/actors/conserving_celerytop~live-career-page-jobs-api/run-sync-get-dataset-items" \
  -H "Authorization: Bearer <YOUR_APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"companies": ["https://boards.greenhouse.io/stripe", "https://jobs.lever.co/palantir"], "department": "engineering"}'
```

In Clay, use the **Run Apify Actor** enrichment with the input `{"companies": [<career page column>]}`, with the column token not in quotes, and choose one summary row per company to keep each cell small. Each row costs 1 Clay action and no Clay data credits, plus $0.01 per company here.

### Live HTTP API

To send companies in one HTTP request and get their jobs in the response, with no run to wait for, use [Live Jobs HTTP API](https://apify.com/conserving_celerytop/live-jobs-http-api). It has the same fields, filters and event prices, and it also charges the Apify platform usage of each request. Normal runs of this Actor charge only the events above.

### Input

```json
{
  "companies": ["stripe", "linear.app", "https://jobs.lever.co/palantir", "https://jobs.ashbyhq.com/openai", "https://ottonova.jobs.personio.com"],
  "companyLists": [],
  "outputMode": "jobs",
  "includeDescription": true,
  "maxJobsPerCompany": 50,
  "onlyNewJobs": false,
  "monitorName": "default",
  "includeUpdatedJobs": false,
  "alertWebhookUrl": "",
  "alertMaxJobs": 10,
  "alertOnFirstCheck": false,
  "titleIncludes": ["engineer", "data scientist"],
  "department": "engineering",
  "location": "London",
  "locationExcludes": ["India"],
  "remoteOnly": false,
  "workplaceTypes": ["remote", "hybrid"],
  "employmentTypes": ["full_time"],
  "seniorities": ["senior", "staff_principal"],
  "jobFunctions": ["engineering", "data"],
  "languages": ["en"],
  "titleExcludes": ["intern", "contract"],
  "descriptionIncludes": ["Snowflake", "dbt"],
  "hasSalary": false,
  "minAnnualSalary": 120000,
  "minAnnualSalaryCurrency": "USD",
  "postedSince": "30 days",
  "postedBefore": "2026-09-20",
  "maxExperienceYears": 5,
  "visaSponsorship": false
}
```

- Give `companies`, `companyLists`, or both. `startUrls` and `urls` are other names for `companies`, so input written for other Actors works too.
- **Title includes**, **Title excludes** and **Description includes** match whole words in any case, so `engineer` matches "Software Engineer" but not "Engineering Manager", and `C++` works too.
- **Location** takes a country name or code (`DE` keeps Berlin), a US state or Canadian province by name, or another place, such as London. For several departments or locations, put one per line, or a line break in JSON (`"engineering\nsales"`). A job that matches any of them is kept. A comma does not split values, so `engineering, sales` is one department.
- Filters on workplace, job type, seniority, language, salary and **Posted before** leave out jobs whose value is unknown, and the company's `warning` says how many.
- **Description includes**, **Max years of experience**, **Only jobs that sponsor visas**, **Languages** and the two salary filters read the description, so they turn on **Include job description**.

#### Ready-made company lists

No list of your own? Pick one or more in **Ready-made company lists** (`companyLists`) and clear **Companies**, or use both.

| List | Companies |
|---|---|
| `ai-companies` | 179 AI labs and AI-first companies |
| `tech-companies` | 311 tech companies |
| `remote-first` | 90 companies with at least 4 in 5 of their open jobs remote |
| `europe-tech` | 65 tech companies with at least 4 in 5 of their open jobs in Europe |

Each company in a list costs $0.01, like one you add, and your filters apply. A company in two lists, or in a list and in **Companies**, is read and charged once. A run takes at most 500 companies, so `ai-companies` and `tech-companies` (490 together) fit in one run, but not with a third list. Every company in these lists had at least 10 open jobs when we checked its job board on September 24 or 25, 2026. We refresh the lists once a month. When a list changes, a monitor that uses it gives each company that joined a first check ($0.01) and stops checking companies that left.

### Output

Each dataset row's `rowType` is `job`, `status` (a company with no job to return) or `company` (a summary row). On later checks with **Only new jobs**, a company whose board was read gets no status row, and any other company gets one only when its status changed. With **Only new jobs**, closed jobs are job rows too. The **Output** tab describes every field. A job row, shortened, looks like this.

```json
{
  "rowType": "job",
  "company": "https://jobs.ashbyhq.com/ashby",
  "companyStatus": "ok",
  "jobKey": "ashby:ashby:390e266b-4b6c-4490-ad74-05ff5e0bb36a",
  "title": "Product Manager, Onboarding and Growth",
  "department": "Product",
  "location": "Remote - US",
  "countryCode": "US",
  "workplaceType": "remote",
  "jobFunction": "product",
  "employmentTypeNormalized": "full_time",
  "salaryMin": 180000,
  "salaryMax": 260000,
  "salaryCurrency": "USD",
  "salaryPeriod": "year",
  "postedAt": "2026-09-11T19:28:18.199Z",
  "url": "https://jobs.ashbyhq.com/ashby/390e266b-4b6c-4490-ad74-05ff5e0bb36a"
}
```

| Field | Values |
|---|---|
| `seniority` | `intern`, `entry`, `mid`, `senior`, `staff_principal`, `lead_manager`, `director`, `vp` or `c_level`, and null when the title gives no level |
| `employmentTypeNormalized` | `full_time`, `part_time`, `contract`, `temporary`, `internship`, `apprenticeship` or `volunteer` |
| `salaryPeriod`, `salaryAnnualMin`, `salaryAnnualMax` | `year`, `month`, `week`, `day` or `hour`, and the pay per year, for comparison |

`jobKey` stays the same in every run, so you can join runs on it. The `COMPANIES` record on the Output tab has one status per company, with `companyTotalOpenJobs`, `companyMatchedJobs`, `companyJobsReturned`, `companyJobsWithoutDate`, `chargedEventCount`, `extraJobBlocksCharged`, `jobDetailBlocksCharged`, `upstreamCalls`, `newJobsCount`, `closedJobsCount`, `updatedJobsCount`, `firstCheck`, `previousCheckAt` and `inputDomain`.

#### One summary row per company

Set **Rows to return** (`outputMode`) to get hiring counts per company instead of, or next to, the job rows.

| `outputMode` | Rows | Price per company |
|---|---|---|
| `jobs` (default) | One row per job | Lookup, plus one extra charge per further 1,000 job rows |
| `companies` | One summary row, no job rows | Lookup only, however many jobs the company has. **Include job description** adds the description price on the boards listed under [pricing](#how-much-does-it-cost) |
| `both` | Job rows plus one summary row | Same as `jobs`. The summary row is free |

Counts use every job that passes your filters, before **Max jobs per company**. A summary row has `openJobs`, `jobsPostedLast7Days`, `jobsPostedLast30Days`, `jobsOpenOver90Days`, `remoteShare`, `engineeringShare`, `salesShare`, `topDepartments`, `topLocations`, `functionCounts`, `functionCountsLast30Days`, `salaryMedians`, `leadershipRoles` and `firstHireRoles`. With **Include job description** it adds `topTools`, `toolCoverage` and `visaSponsorshipShare`, and with **Only new jobs** `newJobs`, `closedJobs`, `repostedJobs`, `medianDaysOpen`, `newFunctions` and `newCountries`.

- `functionCountsLast30Days` looks like `{"sales": 4, "engineering": 9}` and is null when the board gives no posting dates.
- `leadershipRoles` lists up to 5 open director, VP and C-level roles, newest first, with title, job function, level, posted date and link. With **Only new jobs**, roles closed since the previous check fill the rest of the 5, with `status` set to `closed`.
- `firstHireRoles` lists up to 5 first or founding hires. `cue` is `title` when the title says so, and `text` when the description does, which needs **Include job description**.
- `newFunctions` and `newCountries` list the job functions and country codes with open jobs now and none at the previous check, and are null on a first check.

### Statuses and charges

| `companyStatus` | Meaning | Charged |
|---|---|---|
| `ok` | Open jobs match your filters | Yes |
| `no_matching_jobs` | Open jobs exist, but none match your filters | Yes |
| `no_open_jobs` | The board has no open jobs | Yes |
| `not_found` | No board was found under this name or link | Yes |
| `source_error` | The board failed or sent unreadable data | Yes |
| `no_job_board_found` | We read the website but found no job board | Yes |
| `website_unavailable` | We could not open the website | No |
| `unsupported_job_board` | A board or career site we do not read, or a site or employer that opts out. `error` says why | No |
| `invalid_input` | Not a link, website or name, or a board link that names no company | No |
| `duplicate` | Another entry was the same board | No |
| `skipped_time_limit` | Time ran out before this company was read | No |
| `skipped_spending_limit` | Your spending limit could not cover this company's charges | No |
| `skipped_client_disconnected` | Only in Live Jobs HTTP API. Your client disconnected first | No |
| `internal_error` | Something failed on our side. `error` says what | Only if it happened after we read the job board, but not while charging |

### FAQ

#### How is this different from a job search database?

A job search database collects jobs ahead of time, and you search them by keyword, title or place. This Actor reads the job boards of the companies you name when you run it. You get every job each board shows at that moment, including jobs posted months ago that are still open, at any company on the 22 job boards we read. The limit is that you bring the companies, or pick a ready-made list. It does not search all companies by keyword. To look across many companies, pick a ready-made list and set **Title includes**. Each company in the list costs $0.01, even when no job matches.

#### Why is the salary empty?

The company does not publish pay on its board. Turn on **Include job description** to also get pay written in the job text.

#### Why not call the job boards myself?

You can, for one board. This Actor gives you 22 boards with the same fields and filters. It takes a company website or name, not only a board link, and it remembers what you received, so later checks return only new and closed jobs. When a board changes, we update the Actor, and your input and output stay the same.

### Legal and trademarks

This Actor reads only job postings that companies publish on public job boards, with no login. It follows robots.txt on company websites and on Workday and Eightfold sites, and keeps to the pace each board asks for. It is built for job data, not personal data, and it removes email addresses, phone numbers and LinkedIn profile links from descriptions. Check that your use follows the laws and terms that apply to you.

This Actor is not affiliated with or endorsed by Greenhouse, Lever, Ashby, Workday, Eightfold or any other job board. Their names are trademarks of their owners. An employer or job board that wants us to stop reading its site can open an issue, and we add it to our blocklist.

Countries, US states and cities come from GeoNames (`https://www.geonames.org/`), licensed under CC BY 4.0 (`https://creativecommons.org/licenses/by/4.0/`). We shortened the list and added common other names, such as "NYC". Common English words come from SCOWL (`wordlist.aspell.net`), Copyright 2000-2016 Kevin Atkinson, used under its permission notice.

### Changelog and support

We add output fields, but we do not rename or remove them. If a field ever has to change, we will say so on the **Changelog** tab first. Found a problem or need another board? Open an issue on the **Issues** tab. If this Actor saved you time, a short review on its Store page helps other people find it.

# Changelog

This Actor's version history is a separate document: https://apify.com/conserving\_celerytop/live-career-page-jobs-api/changelog.md

# Actor input Schema

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

One entry per company, up to 500. A job board link works best, such as boards.greenhouse.io/stripe, on Greenhouse, Lever, Ashby, Workday, Eightfold, Workable, Personio, Teamtailor, Recruitee, JOIN, Homerun, Gem, JazzHR, Paylocity, Freshteam, PageUp, Polymer, HireHive, HiringThing, Trakstar Hire, ClearCompany or GoHire. A website (stripe.com) works too, as does a plain name (stripe) for many companies. $0.01 per company, 1,000 jobs included, also when no board is found.

## `companyLists` (type: `array`):

Run on ready-made lists of companies instead of, or next to, your own list in Companies. Each company in a list is one company lookup ($0.01, 1,000 jobs included), and filters such as title, location and posted date apply as usual. To run only the lists, clear Companies. A company in two lists, or in a list and in Companies, is read and charged once. At most 500 companies per run in all. The lists are refreshed monthly. API value: a list such as \["ai-companies"].

## `outputMode` (type: `string`):

jobs: one row per job. companies: one summary row per company with open jobs, jobs posted in the last 7 and 30 days, remote share, top departments, locations, countries, seniority, job functions, leadership and first-hire roles, salary medians, top tools, and new and closed jobs with Only new jobs. Counts use every job passing your filters. It costs one lookup per company, never the extra 1,000-job charge. both: job rows plus summary rows (rowType job, company or status).

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

true: add each job's full description as plain text (up to 60,000 characters, no contact details) and the tools it names (field tools). Free on most boards. $0.01 per started 200 jobs on JazzHR, Paylocity, Freshteam, JOIN, Polymer, Workday, Eightfold, ClearCompany, GoHire and HiringThing portals. With Only new jobs, only new jobs are described. At your spending limit, the rest come without one. With Rows to return: companies, summary rows get top tools and first-hire roles.

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

Return at most this many jobs per company, the newest first, for example 50. To answer a quick question, 20 is enough. Leave empty for all jobs, up to 10,000 per company. Set 1,000 or less to never pay the $0.01 charge for each further 1,000 jobs. With Only new jobs, closed jobs come on top of this cap and count toward that charge. Summary rows still count every matching job.

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

true: return only jobs you have not received from this Actor before, plus jobs that closed since your previous check (field change is new or closed). The first check of a company returns all its jobs for $0.01. A later check costs $0.002 per started 1,000 open jobs on the board, so $0.002 for 300 jobs and $0.006 for 2,400. Use it with a schedule. What you received is saved in your own Apify account, per Monitor name and set of filters. Default false.

## `monitorName` (type: `string`):

Only used with Only new jobs. Name of the saved list of jobs you received, so separate watchlists do not mix, for example sales-accounts or competitors. Letters, numbers, - and \_ only, up to 40 characters. Default: default.

## `includeUpdatedJobs` (type: `boolean`):

Only used with Only new jobs. true: also return jobs you received before whose title, location, salary or job type changed since your previous check, with change updated, changedFields and the previous values. Each is one more row, so it counts toward the $0.01 per further 1,000 jobs. Default false: the price stays the same and changes are only counted in summary rows.

## `alertWebhookUrl` (type: `string`):

Only used with Only new jobs. An https link that gets one short message after a check with new or closed jobs, and nothing on days without changes. Paste the incoming webhook link of a Slack, Discord or Teams channel, or a Make, Zapier or n8n webhook, such as https://hooks.slack.com/services/... The message has the counts per company and the jobs with their links. Free. Kept secret. Leave empty for no alert.

## `alertMaxJobs` (type: `integer`):

Only used with Alert webhook URL. How many new and closed jobs the alert lists, one line each with title, location and salary when known, for example 20. More jobs are counted with a link to the dataset. A whole number from 1 to 50. Free. Default 10.

## `alertOnFirstCheck` (type: `boolean`):

Only used with Alert webhook URL. true: also send an alert on the first check of a company, which returns all its open jobs as new. Default false: the first check sends nothing, and later checks alert only on new and closed jobs. Free.

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

Keep only jobs whose title contains one of these words or phrases, ignoring case. Use it to check if a company is hiring for a role, for example data engineer or account executive. Whole words only, so engineer matches Software Engineer but not Engineering Manager. A list of up to 100. Leave empty for all titles.

## `department` (type: `string`):

Keep only jobs whose department, team or department path contains this text, ignoring case, for example engineering. For several, put one per line. A job that matches any of them is kept. In API input, put a line break between values, as in "engineering\nsales". Up to 100. Leave empty for all departments.

## `location` (type: `string`):

Keep only jobs in this place, such as London, California or Germany. A country name or code (Germany, DE) keeps jobs in that country, so DE does not keep Rio de Janeiro. A US state or Canadian province keeps its region, so California keeps San Mateo, CA. Other text matches whole words, so York keeps New York, not Yorkshire. For several, put one per line. Any match keeps a job. In API input, put a line break between values. For remote jobs use Remote only. Leave empty for all.

## `locationExcludes` (type: `array`):

Leave out jobs in any of these places, matched like Location: India or IN also leaves out jobs in Bangalore, and California leaves out San Mateo, CA. A job with several places is left out when any of them matches. Jobs with no location are kept. A list of up to 100, for example India and Brazil. Leave empty to keep every location.

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

true: keep only jobs that can be done fully remote. Hybrid and on-site jobs are left out. Default false.

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

Keep only jobs with one of these workplace types: remote, hybrid or onsite (field workplaceType), from the board's own field or the location text. Jobs whose workplace type is unknown are left out, and the company's warning says how many; Greenhouse marks only remote and hybrid jobs, so its on-site jobs are unknown. Leave empty for all jobs.

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

Keep only jobs with one of these job types (field employmentTypeNormalized), from the board's job type or the title. Jobs whose type is unknown are left out, and the company's warning says how many. Teamtailor gives no job type and Greenhouse only when the company sets one, so there mostly titles like Intern or Part-time tell. Leave empty for all.

## `seniorities` (type: `array`):

Keep only jobs at one of these levels (field seniority), read from the title: Senior Engineer is senior, Head of Sales is director. A title without a level word, such as Software Engineer, has no seniority: those jobs are left out, and the company's warning says how many. Leave empty for all levels.

## `jobFunctions` (type: `array`):

Keep only jobs in these functions, read from each job's title, then its department and team (output field jobFunction). Pick one or more, for example Engineering and Data. English and German titles are read, and basic French, Spanish, Dutch and Swedish ones. In our tests about 9 in 10 jobs got the right function. API value: a list such as \["engineering", "data"]. Leave empty for all functions.

## `languages` (type: `array`):

Keep only jobs written in one of these languages, as two-letter codes such as en, de or fr (field language). Greenhouse gives each job's language; elsewhere it is read from the description, so this turns on Include job description, also with Rows to return: companies, with its price on JazzHR, Paylocity, Freshteam, JOIN, Polymer, Workday, Eightfold, ClearCompany, GoHire and HiringThing portals. Jobs whose language is unknown are left out; the company's warning says how many.

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

Leave out jobs whose title contains any of these words or phrases, ignoring case. Whole words only, so intern does not match International. A list of up to 100, for example senior and intern. An excluded word wins over Title includes. Leave empty to keep every title.

## `descriptionIncludes` (type: `array`):

Keep only jobs whose description names any of these words or phrases, ignoring case, such as Snowflake, Rust or C++. Whole words only, so Rust does not match trust. A tool also matches its other names, so Postgres finds PostgreSQL. Up to 100. This turns on Include job description, also with Rows to return: companies, with its price on JazzHR, Paylocity, Freshteam, JOIN, Polymer, Workday, Eightfold, ClearCompany, GoHire and HiringThing portals. Leave empty for all jobs.

## `hasSalary` (type: `boolean`):

true: keep only jobs with a salary (salaryMin or salaryMax), from the board or written in the description. Many boards have no pay field or show pay only in the description, so this turns on Include job description, also with Rows to return: companies, with its job-details price on JazzHR, Paylocity, Freshteam, JOIN, Polymer, Workday, Eightfold, ClearCompany, GoHire and HiringThing portals. Default false.

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

Keep jobs whose yearly salary (salaryAnnualMax, else salaryAnnualMin) reaches this amount in the currency below, such as 120000. Pay in the description counts too, so this turns on Include job description, with its price on JazzHR, Paylocity, Freshteam, JOIN, Polymer, Workday, Eightfold, ClearCompany, GoHire and HiringThing portals. Jobs paid in another currency (no exchange rates) or with no yearly salary are left out; the warning says how many. Empty for no minimum.

## `minAnnualSalaryCurrency` (type: `string`):

Currency of Minimum yearly salary, as a three-letter code such as USD, EUR or GBP. Only jobs whose salary is in this currency can pass. Used only with Minimum yearly salary. Default USD.

## `postedSince` (type: `string`):

Keep only jobs posted on or after this date. Use a date as YYYY-MM-DD, for example 2026-09-01, or a period as a number plus hours, days, weeks, months or years, for example 24 hours or 7 days. A period counts back from the start of each run, which suits schedules. Jobs whose board gives no posting date are left out. Leave empty for all dates.

## `postedBefore` (type: `string`):

Keep only jobs posted before this date. Use a date as YYYY-MM-DD, for example 2026-06-01, or a period such as 30 days for jobs posted more than 30 days ago. With Posted since it gives a date range. Jobs whose board gives no posting date are left out, and the company's warning says how many. Leave empty for all dates.

## `maxExperienceYears` (type: `integer`):

Keep only jobs whose description asks for at most this many years of experience, read from phrases like 5+ years or mindestens 3 Jahre. A whole number from 0 to 50, such as 3. Jobs that give no years are left out, and the warning says how many. This turns on Include job description, also with Rows to return: companies, with its price on JazzHR, Paylocity, Freshteam, JOIN, Polymer, Workday, Eightfold, ClearCompany, GoHire and HiringThing portals. Leave empty for all.

## `visaSponsorship` (type: `boolean`):

true: keep only jobs whose description says the company sponsors visas. Jobs that do not mention it are left out, and the warning says how many. This reads the description, so it turns on Include job description, also with Rows to return: companies, with its price on JazzHR, Paylocity, Freshteam, JOIN, Polymer, Workday, Eightfold, ClearCompany, GoHire and HiringThing portals. Default false.

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

Another name for Companies, so input written for other Actors works: links as strings or as {"url": "..."} objects. Merged into Companies.

## `urls` (type: `array`):

Another name for Companies, so input written for other Actors works: links as strings or as {"url": "..."} objects. Merged into Companies.

## Actor input object example

```json
{
  "companies": [
    "https://boards.greenhouse.io/stripe",
    "linear.app",
    "openai"
  ],
  "outputMode": "jobs",
  "includeDescription": false,
  "maxJobsPerCompany": 50,
  "onlyNewJobs": false,
  "monitorName": "default",
  "includeUpdatedJobs": false,
  "alertMaxJobs": 10,
  "alertOnFirstCheck": false,
  "titleIncludes": [
    "engineer"
  ],
  "department": "engineering",
  "location": "London",
  "remoteOnly": false,
  "jobFunctions": [
    "engineering",
    "data"
  ],
  "descriptionIncludes": [
    "Snowflake",
    "dbt"
  ],
  "hasSalary": false,
  "minAnnualSalaryCurrency": "USD",
  "postedSince": "7 days",
  "visaSponsorship": false
}
```

# Actor output Schema

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

Dataset items, one per job: company, companyName, matchedBy, companyStatus, title, department, team, location, countryCode, city, remote, seniority, jobFunction, employment type, salary with its yearly value, tools (with descriptions), postedAt and url. The dataset has more fields (jobId, applyUrl, description, country, region, salaryRanges, charged, warning, error); read it with fields=... to keep only what you need.

## `companies` (type: `string`):

JSON array, one object per company in input order: companyStatus (ok, no\_open\_jobs, not\_found, no\_job\_board\_found, invalid\_input and others), charged, chargedEvent, companyName, matchedBy (link, website, directory, name or name variant), inputDomain and boardUrl for websites, companyTotalOpenJobs, companyMatchedJobs, companyJobsReturned, newJobsCount, closedJobsCount, updatedJobsCount, error and warning.

## `companySummaries` (type: `string`):

Dataset items with rowType company, one per company, when Rows to return is companies or both: openJobs, jobs posted in the last 7 and 30 days, jobs open over 90 days, remote, engineering and sales shares, top departments, locations and countries, seniority and job function counts, jobs per function posted in the last 30 days, leadership and first-hire roles, top tools, salary medians, and new, closed, updated and reposted jobs with Only new jobs. With jobs, this view has no summary rows.

# 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/stripe",
        "https://jobs.lever.co/palantir",
        "https://jobs.ashbyhq.com/ashby"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("conserving_celerytop/live-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": [
        "https://boards.greenhouse.io/stripe",
        "https://jobs.lever.co/palantir",
        "https://jobs.ashbyhq.com/ashby",
    ] }

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

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,conserving_celerytop/live-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/r9Q1czvsAoCBZUURz/builds/1IqxeoTGg5y8lcl6H/openapi.json
