# Career Page Jobs Finder (Auto-detect ATS) (`datacolada/career-page-jobs-finder`) Actor

Give it company websites and get every open job from their own career page. Auto-detects Greenhouse, Lever, Ashby, Workable, Recruitee, SmartRecruiters, Workday or schema.org JobPosting, returns one clean format, and can output only new jobs since the last run.

- **URL**: https://apify.com/datacolada/career-page-jobs-finder.md
- **Developed by:** [Cyd Dunscombe](https://apify.com/datacolada) (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 Finder (Auto-detect ATS)

Give it a list of company websites. It finds each company's job board, works out which applicant tracking system (ATS) the company uses, and returns every open job in one consistent format: title, department, location, remote, employment type, posting date, salary (when published), apply link and full description.

You don't need to know if a company uses Greenhouse, Lever, Ashby, Workday or something else. Paste `stripe.com`, `notion.so` or `nvidia.com`, and the Actor works it out.

Jobs come straight from each company's own public job board API, so they are as fresh as the company's own careers page. No aggregator delay, no duplicates from reposts, and no login needed.

### How to get jobs from career pages in 3 steps

1. Paste company websites (`stripe.com`), company names' domains, or job-board links into **Companies**.
2. Optionally add filters: title keywords, locations, remote only, posted in the last N days.
3. Click **Start**. Jobs appear in the **Output** tab; export them as CSV, Excel or JSON, or turn on **Only new jobs** and schedule it for a daily job alert.

### What you can use it for

- **Job seekers and job alerts:** watch 50 target companies and get only the *new* jobs that match your keywords, every morning.
- **Recruiting and sales intelligence:** see which companies are hiring for which roles, as a signal for outreach (for example, "companies hiring their first data engineer").
- **Job boards and niche aggregators:** fill a niche board (climate, AI, remote, a city) directly from employers' sites.
- **Market research:** track hiring trends by department, location and seniority across a list of companies.
- **AI agents and RAG:** clean plain-text job descriptions ready for LLM matching, summarizing or tagging.

### Supported job boards

| ATS | How it's detected |
| --- | --- |
| Greenhouse | links/embeds on the site, direct board URL, or name guess |
| Lever | links on the site, direct URL, or name guess |
| Ashby | links/embeds on the site, direct URL, or name guess |
| Workday | links on the site or direct `*.myworkdayjobs.com` URL |
| SmartRecruiters | links on the site, direct URL, or name guess |
| Workable | links/embeds on the site, direct URL, or name guess |
| Recruitee | links on the site, direct URL, or name guess |
| Any site with `JobPosting` structured data | read from the careers page (schema.org JSON-LD) |

How detection works: the Actor opens the homepage, follows links that look like "Careers" or "Jobs" on the same domain, and looks for links to a supported ATS. If it finds none, it reads `JobPosting` structured data on the careers page. As a last resort (switch off with **Guess the job board from the company name**) it tries the company name on each ATS.

You can always paste a direct board URL instead (for example `https://jobs.lever.co/palantir` or `https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite`); then nothing has to be detected.

### Input example

```json
{
  "companies": ["stripe.com", "notion.so", "https://jobs.lever.co/palantir", "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite"],
  "titleKeywords": ["engineer", "data scientist"],
  "excludeTitleKeywords": ["intern"],
  "locationKeywords": ["United States", "Remote"],
  "postedWithinDays": 14,
  "maxJobsPerCompany": 200
}
```

Filters are case-insensitive. Within each filter *any* keyword can match; different filters must all match. Location keywords match whole words, so `US` matches "US, CA, Santa Clara" but not "Russia".

#### New jobs only (monitoring)

Turn on **Only new jobs** and save the input as a Task with a Schedule (for example daily). The first run returns all matching jobs and remembers them. Each later run returns only jobs that weren't there before, so you only pay for new jobs. Use a different **Monitor name** for each separate watch list.

### Output

One dataset row per job:

```json
{
  "company": "Bosch Group",
  "companyInput": "https://jobs.smartrecruiters.com/BoschGroup",
  "ats": "smartrecruiters",
  "boardUrl": "https://careers.smartrecruiters.com/BoschGroup",
  "detectionMethod": "direct-ats-url",
  "jobId": "744000158143959",
  "title": "Manufacturing Engineer - Automated Assembly",
  "department": "Manufacturing",
  "location": "Charleston, SC, United States",
  "country": "US",
  "remote": false,
  "workplaceType": "onsite",
  "employmentType": "Full-time",
  "postedAt": "2026-10-27T20:22:15.673Z",
  "url": "https://jobs.smartrecruiters.com/BoschGroup/744000158143959",
  "applyUrl": "https://jobs.smartrecruiters.com/BoschGroup/744000158143959-manufacturing-engineer-automated-assembly?oga=true",
  "salary": null,
  "descriptionText": "Do you want beneficial technologies being shaped by your ideas? ...",
  "isNew": null,
  "scrapedAt": "2026-10-28T02:38:12.165Z"
}
```

`salary` is filled when the board publishes pay in structured form (Ashby, Lever, Recruitee and JSON-LD boards). On other boards, pay ranges usually appear inside `descriptionText`. `isNew` is `true` in monitoring mode.

The key-value store also has:

- `COMPANIES`: one entry per input company, with the detected ATS, how it was found, how many jobs were found, matched and saved, or the error.
- `RUN_SUMMARY`: totals for the run.

Export to JSON, CSV, Excel or connect to Google Sheets, Make, Zapier, n8n or your own code via the Apify API.

### Pricing

Pay per event. You only pay for results.

| Event | Price |
| --- | --- |
| Company job board read (one company whose board was found and read) | $0.003 |
| Job (one job saved to the dataset, after your filters) | $0.0015 |

Companies with no supported job board, blocked addresses and errors are free. Jobs removed by your filters, and already-seen jobs in "Only new jobs" mode, are free.

Example: 50 companies, 10 matching jobs each = 50 × $0.003 + 500 × $0.0015 = $0.90. A daily monitor of the same 50 companies with ~20 new jobs a day costs about $0.18 per day.

You can cap any run with the "Max total charge" setting; the Actor stops cleanly at the cap.

### Integrations and API

- **Daily new-jobs feed:** save the input as a Task with **Only new jobs** on, and add a daily Schedule.
- **Google Sheets, Make, Zapier, n8n:** use their Apify connectors to start the Task and append new jobs to a sheet, CRM or Slack channel.
- **API / code:**

```bash
curl -X POST "https://api.apify.com/v2/acts/datacolada~career-page-jobs-finder/run-sync-get-dataset-items?token=YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"companies":["stripe.com","https://jobs.lever.co/palantir"],"titleKeywords":["engineer"],"includeDescription":false}'
```

```python
from apify_client import ApifyClient
client = ApifyClient("YOUR_API_TOKEN")
run = client.actor("datacolada/career-page-jobs-finder").call(run_input={
    "companies": ["stripe.com", "notion.so"],
    "titleKeywords": ["engineer"],
    "onlyNewJobs": True,
})
for job in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(job["company"], job["title"], job["location"], job["url"])
```

### Tips

- Big employers have thousands of jobs. Use title and location keywords: for Workday and SmartRecruiters boards they are sent to the board's own search, which keeps runs fast.
- If a company isn't detected, paste its job board URL directly. The `COMPANIES` record tells you why a company wasn't found.
- Turn off **Include full description** if you only need titles and links; runs are faster.
- Some companies host several boards (for example per region or brand). Add each board URL separately.

### FAQ

**Do I need to know which ATS a company uses?** No. Paste the company website and the Actor finds the job board. Check the `COMPANIES` record to see what was detected for each company.

**Which ATS platforms are supported?** Greenhouse, Lever, Ashby, Workday, SmartRecruiters, Workable, Recruitee, plus any careers page that publishes schema.org `JobPosting` data.

**How fresh is the data?** Jobs are read live from each company's own job board at run time, not from a cached database.

**What does a run cost?** $0.003 per company whose board was read, plus $0.0015 per job saved after your filters. Use filters and **Max jobs per company** to keep runs small.

**Can I get only new jobs?** Yes. Turn on **Only new jobs since the last run** and schedule the Actor. Jobs seen before are skipped and not charged.

**A company was not found. What can I do?** Paste its job-board URL directly (for example `https://boards.greenhouse.io/company` or a `myworkdayjobs.com` link).

### Limits and fair use

The Actor reads only public job listings that companies publish for applicants, through the public job-board endpoints of each ATS. It does not log in, apply to jobs, or collect applicant data. Only public http(s) websites are opened; private, loopback and cloud-metadata addresses are refused. Please respect each site's terms of use.

Job boards behind custom career sites with no supported ATS and no `JobPosting` data (for example fully custom in-house systems) are reported as "No supported job board found" and are not charged.

### Changelog

- 0.1.3 README FAQ and API examples; lighter prefilled example so the first run is fast and cheap.
- 0.1 First release: Greenhouse, Lever, Ashby, Workday, SmartRecruiters, Workable, Recruitee and JSON-LD; new-jobs-only monitoring.

# Actor input Schema

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

Company websites or domains (stripe.com, https://www.notion.so) or direct job-board URLs (https://jobs.lever.co/palantir, https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite). The Actor finds the job board for each one.

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

Keep only jobs whose title contains at least one of these words, case-insensitive. Leave empty for all jobs.

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

Drop jobs whose title contains any of these words, e.g. intern, senior.

## `locationKeywords` (type: `array`):

Keep only jobs whose location, country or workplace type contains one of these, e.g. London, Germany, Remote.

## `departmentKeywords` (type: `array`):

Keep only jobs whose department or team contains one of these, e.g. Engineering, Sales.

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

Keep only jobs marked remote by the job board.

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

Keep only jobs posted in the last N days (0 = any date). Jobs without a posting date are kept.

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

Remembers job IDs in a named key-value store and outputs only jobs not seen before. Schedule the Actor daily for a new-jobs feed. The first run outputs everything.

## `monitorStoreName` (type: `string`):

Name of the key-value store that remembers seen jobs. Use different names for different watch lists.

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

Adds the plain-text job description. Turn off for faster runs and smaller output.

## `maxDescriptionChars` (type: `integer`):

Longer descriptions are cut. 0 = no limit.

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

Upper limit of jobs saved per company after filters.

## `allowSlugGuess` (type: `boolean`):

If the website has no visible job-board link, try the company name on the public Greenhouse, Lever, Ashby, Workable, Recruitee and SmartRecruiters APIs. Results are marked detectionMethod = slug-guess.

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

How many companies to process at the same time.

## Actor input object example

```json
{
  "companies": [
    "stripe.com",
    "notion.so",
    "https://jobs.lever.co/palantir"
  ],
  "titleKeywords": [
    "product designer"
  ],
  "remoteOnly": false,
  "postedWithinDays": 0,
  "onlyNewJobs": false,
  "monitorStoreName": "career-jobs-monitor",
  "includeDescription": true,
  "maxDescriptionChars": 20000,
  "maxJobsPerCompany": 500,
  "allowSlugGuess": true,
  "maxConcurrency": 4
}
```

# Actor output Schema

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

Dataset rows: company, title, department, location, remote, employmentType, postedAt, url, applyUrl, salary, descriptionText, ats, isNew.

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

Per company: detected ATS, board URL, how it was detected, jobs found / matched / saved, or why no board was found.

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

Totals: companies resolved, not found, failed; jobs found, matched, saved, new.

# 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": [
        "stripe.com",
        "notion.so",
        "https://jobs.lever.co/palantir"
    ],
    "titleKeywords": [
        "product designer"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("datacolada/career-page-jobs-finder").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": [
        "stripe.com",
        "notion.so",
        "https://jobs.lever.co/palantir",
    ],
    "titleKeywords": ["product designer"],
}

# Run the Actor and wait for it to finish
run = client.actor("datacolada/career-page-jobs-finder").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": [
    "stripe.com",
    "notion.so",
    "https://jobs.lever.co/palantir"
  ],
  "titleKeywords": [
    "product designer"
  ]
}' |
apify call datacolada/career-page-jobs-finder --silent --output-dataset

```

## MCP server setup

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

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/3XQc1IQLhlE7pblrg/builds/8qVT4RkkodNkPDlDb/openapi.json
