# Career Page Jobs Scraper: Company Name or Website to Jobs (`gefese/company-careers-jobs-scraper`) Actor

Company names or websites in, open jobs out: finds the careers page, detects the applicant tracking system (Workday, Greenhouse, Lever, Ashby and 10 more, 14 in all) and returns one job format. Unofficial: not affiliated with any ATS vendor or employer.

- **URL**: https://apify.com/gefese/company-careers-jobs-scraper.md
- **Developed by:** [Gonzalo Fajardo](https://apify.com/gefese) (community)
- **Categories:** Jobs, Lead generation
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.54 / 1,000 result items

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

## Career Page Jobs Scraper: Company Name or Website to Jobs

Type company names or paste company websites. For each one this Actor finds the careers page, detects which applicant tracking system (ATS) hosts the job board, and returns the open jobs in one common format. It reads plain HTML and public job feeds only: no browser, no login, no proxy. It is unofficial and not affiliated with any ATS vendor or employer.

### What does Career Page Jobs Scraper do?

For every company website, and for every company name after it has been matched to a website, the Actor:

1. Fetches the company's `robots.txt` and the homepage.
2. Looks for links to a supported ATS in the page source (links, iframes, scripts). If there are none, it follows up to a few same-site pages that look like careers pages (for example `/careers`, `/jobs`, "Join us", "Karriere", "Empleo") and looks there. A careers link that redirects straight to an ATS counts too.
3. If the pages show no board, it guesses (see "Name guess" below): the company name is tried as a board name on Greenhouse, Lever and Ashby.
4. Reads the open jobs from the detected board with the matching vendor reader.

Supported ATS (14): Workday, Greenhouse, Lever, Ashby, Workable, Recruitee, Personio, Dayforce, Eightfold, Zoho Recruit, ClearCompany (apply.clearcompany.com, formerly hrmdirect.com), Cornerstone, Jobvite and Rippling. If you already know the career-site address, paste it into `companyUrls` instead of the company website (for example `https://jobs.lever.co/spotify`, `https://jobs.jobvite.com/nutanix/jobs`, `https://ats.rippling.com/rippling/jobs` or `https://apply.clearcompany.com/pmic`) and it is read directly.

Every dataset item is one job with these fields: `vendor`, `sourceUrl`, `company`, `jobId`, `title`, `location`, `locations`, `department`, `employmentType`, `remote`, `postedDate`, `salary` (only where the employer publishes it), `description` (plain text, optional), `jobUrl`, `scrapedAt`, plus `companyInput` (the name or URL you gave), `companyUrl` (the website that was scanned, null when a board was found by name only), `resolvedDomain` (the website domain the input was matched to), `careersPageUrl` (the page where the ATS link was found), `atsVendor`, `atsBoardUrl` and `detectionMethod` (how the board was found: `page_link`, `redirect`, `direct_url`, `domain_guess` or `name_guess`).

A second output, the key-value store record `DETECTION`, lists for every input: the text you gave, whether it was a name or a URL, the status (`detected`, `not_detected`, `robots_disallowed`, `blocked`, `fetch_failed`), the resolved domain, the vendor, the detection method, the board URLs, the careers page and the number of jobs returned. It is free. The run's status message summarises it, for example "4 of 4 companies detected, 20 jobs".

### How to run it

1. Open the Actor and keep the prefilled input: two company names (Figma, Palantir) and two company websites (Notion, NVIDIA), 5 jobs each.
2. Click Start. The prefill finishes in well under two minutes.
3. Open the Output tab for the jobs (table, JSON, CSV, Excel) and the key-value store for `DETECTION`.
4. Replace the input with the employers you track: type their names in `companyNames`, paste websites in `companyUrls`, or use both. Set `maxItemsPerCompany` to the number of jobs you want per company, and optionally `searchQuery` to filter by job title. One of the two lists must not be empty.

### How are company names turned into job boards?

Names are resolved by fixed rules, without a search engine or any third-party lookup:

1. From the name the Actor builds up to six candidate domains: the name with spaces removed (`Best Buy` gives `bestbuy.com`), the hyphenated form (`best-buy.com`), and forms with a leading "The" and legal endings such as Inc, LLC, Ltd, GmbH, plc, Corp, Corporation, Company, Co, S.A. or AG removed. `.com` is tried first; `.io`, `.ai` and `.co` are tried only if no `.com` candidate was accepted.
2. A candidate is accepted only if its homepage answers with a success status (after redirects) and the page's `<title>` or `og:site_name` names the company: the site name, or a whole segment of the title such as the part after "|", must equal the name once punctuation, spaces and capitals are ignored. A page that merely mentions the name ("name.co for sale") is rejected. A homepage that redirects to a different domain is accepted only if that domain's name still fits the company name.
3. On the accepted website the normal careers-page and ATS detection runs. Items found this way carry `detectionMethod: "domain_guess"`.
4. If no website is accepted, or the website shows no supported ATS, the name is tried as a board name on Greenhouse, Lever and Ashby (`name_guess`).

Homepages that answer 403 or similar are skipped, never retried with tricks. Names are matched to the company that owns the domain with that name; they can be wrong, see the FAQ.

### How much does it cost to scrape company careers pages?

The price is 0.0022 USD per job returned (one `result` event per dataset item). 1,000 jobs cost 2.20 USD; 5 jobs from each of 4 companies cost 0.044 USD. Companies where no ATS is detected, or that block automated access, return no items and are not charged. Resolving a name costs nothing extra. You can cap spending with the maximum total charge setting of the run: the Actor stops as soon as the limit is reached and makes no further requests.

### Input example

```json
{
  "companyUrls": [
    { "url": "https://www.notion.com" },
    { "url": "https://www.nvidia.com" }
  ],
  "companyNames": ["Figma", "Palantir"],
  "searchQuery": "",
  "maxItemsPerCompany": 5,
  "includeDescription": true,
  "maxPagesPerCompany": 4,
  "guessBoardsFromDomain": true
}
```

### Output example

Four items from local runs on 2026-10-14 (v0.4). Descriptions are shortened here, the dataset holds the full text. The first three come from the prefill input above: a website (`page_link`) and two names (`domain_guess`). The last one is the name "Block" in a run with `includeDescription` off: no website was accepted, so the board was found by name only (`name_guess`, no `companyUrl`, no `resolvedDomain`).

```json
[
  {
    "vendor": "ashby",
    "sourceUrl": "https://jobs.ashbyhq.com/notion",
    "company": "notion",
    "jobId": "1fc309c8-da20-4ff2-84c7-8b863ece2b0a",
    "title": "Software Engineer, Developer Platform",
    "location": "San Francisco, California",
    "locations": [
      "San Francisco, California",
      "New York, New York"
    ],
    "department": "Engineering",
    "employmentType": "Full time",
    "remote": true,
    "postedDate": "2026-08-24",
    "salary": null,
    "description": "WHO WE ARE  Notion is the collaborative AI workspace where teams and agents think together https://w ...",
    "jobUrl": "https://jobs.ashbyhq.com/notion/1fc309c8-da20-4ff2-84c7-8b863ece2b0a",
    "scrapedAt": "2026-10-14T12:40:32Z",
    "companyInput": "https://www.notion.com",
    "companyUrl": "https://www.notion.com",
    "resolvedDomain": "notion.com",
    "careersPageUrl": "https://www.notion.com/careers",
    "atsVendor": "ashby",
    "atsBoardUrl": "https://jobs.ashbyhq.com/notion",
    "detectionMethod": "page_link"
  },
  {
    "vendor": "greenhouse",
    "sourceUrl": "https://boards.greenhouse.io/figma",
    "company": "Figma",
    "jobId": "5426468004",
    "title": "Account Executive, Enterprise",
    "location": "San Francisco, CA • New York, NY • United States",
    "locations": [
      "San Francisco, CA • New York, NY • United States"
    ],
    "department": "Sales",
    "employmentType": null,
    "remote": null,
    "postedDate": "2025-01-28",
    "salary": "USD 165,000 - 190,000 (Annual Base Salary Range:)",
    "description": "Figma is growing our team of passionate creatives and builders on a mission to make design accessibl ...",
    "jobUrl": "https://boards.greenhouse.io/figma/jobs/5426468004?gh_jid=5426468004",
    "scrapedAt": "2026-10-14T12:40:50Z",
    "companyInput": "Figma",
    "companyUrl": "https://figma.com/",
    "resolvedDomain": "figma.com",
    "careersPageUrl": "https://www.figma.com/careers/",
    "atsVendor": "greenhouse",
    "atsBoardUrl": "https://boards.greenhouse.io/figma",
    "detectionMethod": "domain_guess"
  },
  {
    "vendor": "lever",
    "sourceUrl": "https://jobs.lever.co/palantir",
    "company": "palantir",
    "jobId": "6ed76ce8-4156-4b60-b120-403538bd66cd",
    "title": "Administrative Business Partner",
    "location": "Singapore, Singapore",
    "locations": [
      "Singapore, Singapore"
    ],
    "department": "Administrative",
    "employmentType": "Full-time",
    "remote": false,
    "postedDate": "2026-08-11",
    "salary": null,
    "description": "A World-Changing Company   Palantir builds the world’s leading software for data-driven decisions an ...",
    "jobUrl": "https://jobs.lever.co/palantir/6ed76ce8-4156-4b60-b120-403538bd66cd",
    "scrapedAt": "2026-10-14T12:40:57Z",
    "companyInput": "Palantir",
    "companyUrl": "https://palantir.com/",
    "resolvedDomain": "palantir.com",
    "careersPageUrl": "https://www.palantir.com/careers",
    "atsVendor": "lever",
    "atsBoardUrl": "https://jobs.lever.co/palantir",
    "detectionMethod": "domain_guess"
  },
  {
    "vendor": "greenhouse",
    "sourceUrl": "https://boards.greenhouse.io/block",
    "company": "Block",
    "jobId": "5317296008",
    "title": "Account Manager, SMB",
    "location": "Sydney, Australia",
    "locations": [
      "Sydney, Australia"
    ],
    "department": null,
    "employmentType": null,
    "remote": null,
    "postedDate": "2026-07-09",
    "salary": null,
    "jobUrl": "http://block.xyz/careers/jobs/5317296008?gh_jid=5317296008",
    "scrapedAt": "2026-10-14T12:34:57Z",
    "companyInput": "Block",
    "companyUrl": null,
    "resolvedDomain": null,
    "careersPageUrl": null,
    "atsVendor": "greenhouse",
    "atsBoardUrl": "https://boards.greenhouse.io/block",
    "detectionMethod": "name_guess"
  }
]
```

A `DETECTION` record entry for a name looks like this:

```json
{
  "input": "Palantir",
  "inputType": "name",
  "inputUrl": "https://palantir.com/",
  "resolvedDomain": "palantir.com",
  "status": "detected",
  "reason": "homepage of www.palantir.com is titled \"Home | Palantir\"",
  "vendor": "lever",
  "detectionMethod": "domain_guess",
  "boardUrls": ["https://jobs.lever.co/palantir"],
  "careersPageUrl": "https://www.palantir.com/careers",
  "jobsPushed": 5
}
```

How the two newer readers work: Jobvite career sites are read from their public listing pages (50 jobs per page, followed page by page) and one public job page per returned job, so department, location and description come from the job page; posting date and employment type appear where the site publishes them. Rippling boards are read from the board's public JSON listing; the listing has one row per job and location, and the Actor merges the rows of a job into one item with all its `locations`. With `includeDescription` on, one more public request per job adds the description, employment type, posting date and published pay range.

### Name guess: what it is and how to turn it off

Many company websites do not show their job board in the page source. When the page scan finds no board, or no website was found for a name, and `guessBoardsFromDomain` is on (the default), the Actor takes the name (for a website: the domain name, `shopify.com` becomes `shopify`; for a name: its compact and hyphenated forms) and asks three public job APIs whether a board with that name exists: Greenhouse, Lever and Ashby. A board counts only if it lists at least one job. Greenhouse wins when its board name equals the company name; otherwise Lever, then Ashby. That is 3 to 4 extra requests per tried name, all robots.txt-checked GET requests.

A guess can be wrong: **a `name_guess` board can belong to a different organisation with the same name.** Items found this way carry `detectionMethod: "name_guess"`, and the DETECTION record gives the board name, the number of jobs and the first job title so you can check it. To use only boards that a company's own pages link to, set `guessBoardsFromDomain` to `false`; then `name_guess` never appears (names that do not resolve to a website return nothing). You can also filter the dataset on `detectionMethod`.

### Limitations and the free plan

- Names that are short, common words (for example "Square" or "Block") can belong to several organisations. The Actor accepts a website only when the page names the company exactly, but the owner of that domain may still be a different organisation: check `resolvedDomain` and `jobUrl`. Companies whose website blocks automated access, or whose robots.txt disallows the homepage, cannot be resolved by name through the website; only the name guess is left for them.
- Detection works only when the link to the ATS is present in the raw HTML of the homepage or of a careers page. Sites that build their job list with JavaScript, host their own job database, or use an unsupported ATS are reported as `not_detected`. Measured on 2026-10-14 with v0.4 on 20 company names chosen beforehand (US employers, tech companies of mid size, European companies, several with ambiguous names), the matches were: 13 found and correct, 0 found and wrong, 7 not found (the DETECTION record shows why). A second list of 10 names gave 5 found and correct, 0 wrong. In these runs 12 of the 18 correct matches came from the `name_guess` step (often after the website was found but did not show its board) and 6 from the website's careers page (`domain_guess`). Expect many companies to be `not_detected`.
- Jobvite and Rippling were added in v0.3. Jobvite sites on the company's own domain (for example a careers host that points at Jobvite) are only found when the page links to `jobs.jobvite.com/<company>`; older `hire.jobvite.com` addresses are not read. A Rippling board with no open roles returns no items. The name guess does not cover these two vendors.
- Sites that answer 403, 429 or 503, or show a challenge page, are skipped with status `blocked`. Nothing is retried and no proxy or header trick is used.
- `robots.txt` is enforced for every host that is fetched, including candidate homepages, the careers page host and the ATS host. Disallowed pages are not requested. A `Crawl-delay` is honoured.
- The Actor waits at least one second between requests to the same host and runs one request at a time. A company costs a few seconds to a minute depending on the vendor and on whether descriptions are included; the 20-name run above took 5 minutes in total (about 15 seconds per name on average, one name after another), so large name lists need a longer run.
- A company can have up to 3 boards; the most referenced board is read first, and `maxItemsPerCompany` applies to the company as a whole.
- Salary is filled only where the employer publishes it. Some vendors use the title for `searchQuery` filtering rather than a native search.
- The prefill returns 20 jobs and costs 0.044 USD at the per-job price, so the monthly credit of a free Apify plan can pay for it; larger runs are charged per job as above.

### FAQ

**Should I use names or URLs?** Names are quicker to type and fit tools that only know the company name. Websites are the precise option: use `companyUrls` when a name could be ambiguous or when you already know the site. You can give both lists in one run.

**How do I know a name matched the right company?** Look at `detectionMethod`, `resolvedDomain` and `jobUrl` in each item. `domain_guess` means the name was matched to a website whose title names the company and the board was read from that site's careers page. `name_guess` means only a board with the same name was found: check that the job links lead to the company you meant. `page_link`, `redirect` and `direct_url` come from a website or career-site URL that you gave yourself.

**Does it collect personal data?** No. It reads job-ad fields (title, location, department, published pay, description). E-mail addresses found in job descriptions are replaced by `[email removed]`, and recruiter or contact fields are never read.

**Does it log in or solve captchas?** No. Only anonymous, public pages and public job feeds are used.

**Why was my company `not_detected`?** Its ATS may be unsupported, its website may block automated access, or its job list is rendered by JavaScript. Pass the career-site address directly in `companyUrls` if you know it, or raise `maxPagesPerCompany`. The name guess only covers Greenhouse, Lever and Ashby boards named like the company.

**Where do I report a problem?** Use the Issues tab of this Actor on Apify.

# Actor input Schema

## `companyNames` (type: `array`):

Company names, one per employer (for example Figma or Best Buy). The actor builds a few candidate domains from each name, accepts one only if its homepage title or site name contains the name, then finds the careers page and reads the jobs. If no website is accepted, it tries the name as a board name on Greenhouse, Lever and Ashby. A name can match the wrong company: check detectionMethod and resolvedDomain in the output, or use companyUrls for a precise match.

## `companyUrls` (type: `array`):

Company websites, one per employer (for example https://www.notion.com). The actor finds the careers page, detects the applicant tracking system and reads the open jobs. A URL that already points at a supported career site (Workday, Greenhouse, Lever, Ashby, Workable, Recruitee, Personio, Dayforce, Eightfold, Zoho Recruit, ClearCompany, Cornerstone, Jobvite, Rippling) is read directly; for ClearCompany that is apply.clearcompany.com/<tenant>, <tenant>.clearcompany.com/careers or the former <tenant>.hrmdirect.com address. URLs are the precise option: use them when a company name could be ambiguous. Companies on other systems, or whose site blocks automated access, are reported in the DETECTION record and cost nothing.

## `searchQuery` (type: `string`):

Optional keyword. Dayforce, Eightfold, Cornerstone, Workable and Workday apply it with the career site's own search; for the other vendors every word must appear in the job title (case-insensitive). Leave empty for all jobs.

## `maxItemsPerCompany` (type: `integer`):

Stops after this many jobs for each company website. Each job is one billed result.

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

Adds the plain-text description. For Eightfold, Zoho Recruit, Workable and Workday this costs one extra request per job, so runs are slower (ClearCompany returns the description with the job list).

## `maxPagesPerCompany` (type: `integer`):

After the homepage, at most this many same-site pages (careers or jobs links, then /careers and /jobs) are fetched to look for the ATS link.

## `guessBoardsFromDomain` (type: `boolean`):

When no ATS link is found on the website (or no website was found for a name), try the company name or domain name (for example shopify.com -> shopify, Best Buy -> bestbuy) as the board name on Greenhouse, Lever and Ashby (3 to 4 extra requests per company). A board is accepted only if it lists at least one job. The guess can match a different organisation with the same name; such items have detectionMethod name_guess. Turn this off to use only boards linked from the company's own pages.

## Actor input object example

```json
{
  "companyNames": [
    "Figma",
    "Palantir"
  ],
  "companyUrls": [
    {
      "url": "https://www.notion.com"
    },
    {
      "url": "https://www.nvidia.com"
    }
  ],
  "searchQuery": "",
  "maxItemsPerCompany": 5,
  "includeDescription": true,
  "maxPagesPerCompany": 4,
  "guessBoardsFromDomain": true
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `detection` (type: `string`):

No description

# 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 = {
    "companyNames": [
        "Figma",
        "Palantir"
    ],
    "companyUrls": [
        {
            "url": "https://www.notion.com"
        },
        {
            "url": "https://www.nvidia.com"
        }
    ],
    "searchQuery": "",
    "maxItemsPerCompany": 5,
    "includeDescription": true,
    "maxPagesPerCompany": 4,
    "guessBoardsFromDomain": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("gefese/company-careers-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 = {
    "companyNames": [
        "Figma",
        "Palantir",
    ],
    "companyUrls": [
        { "url": "https://www.notion.com" },
        { "url": "https://www.nvidia.com" },
    ],
    "searchQuery": "",
    "maxItemsPerCompany": 5,
    "includeDescription": True,
    "maxPagesPerCompany": 4,
    "guessBoardsFromDomain": True,
}

# Run the Actor and wait for it to finish
run = client.actor("gefese/company-careers-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 '{
  "companyNames": [
    "Figma",
    "Palantir"
  ],
  "companyUrls": [
    {
      "url": "https://www.notion.com"
    },
    {
      "url": "https://www.nvidia.com"
    }
  ],
  "searchQuery": "",
  "maxItemsPerCompany": 5,
  "includeDescription": true,
  "maxPagesPerCompany": 4,
  "guessBoardsFromDomain": true
}' |
apify call gefese/company-careers-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,gefese/company-careers-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/vekfw94oPvPGLiQEK/builds/P6DyFQk3Myr3QADAu/openapi.json
