# Company Jobs Scraper for 5 ATS Platforms (`samvelkarapetyan/company-jobs-scraper`) Actor

Search a company's jobs across Greenhouse, BambooHR, Comeet, JazzHR, and Jobvite. Get verified career links, ATS job counts, and standardized openings for recruiting research, lead generation, job boards, JSON/CSV exports, and API workflows. Actively maintained, with more ATS platforms planned.

- **URL**: https://apify.com/samvelkarapetyan/company-jobs-scraper.md
- **Developed by:** [Samo Karapetyan](https://apify.com/samvelkarapetyan) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 results

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

## Company Jobs Scraper for Greenhouse, BambooHR, Comeet, JazzHR & Jobvite

Use this **company jobs scraper** and **ATS job scraper API** to search a company name once and get its verified career pages, open-job counts and standardized vacancies across Greenhouse, BambooHR, Comeet, JazzHR and Jobvite. Build recruiting research, job-market intelligence, lead generation, job boards and automated JSON/CSV/API workflows without finding each career page manually.

### Actively maintained and expanding

This Actor is actively maintained. We will continue improving its discovery and adding support for more ATS and career platforms while keeping the standardized output stable. Have a platform or improvement to suggest? [Open a request in the Issues tab](https://apify.com/samvelkarapetyan/company-jobs-scraper/issues). We'll be happy to review customer feedback and prioritize additions that benefit the community.

### Pricing

The launch price is **$0.01 per run plus $1.50 per 1,000 saved jobs** ($0.0015 per Dataset item). Platform usage is included in the event price. An optional `maxItems` input controls the maximum number of paid job rows returned in a run; platform metadata still reports the full counts found on verified boards.

### Input

```json
{
  "filters": {"companyName": "Webflow"},
  "platforms": ["greenhouse", "bamboohr", "comeet", "jazzhr", "jobvite"]
}
```

Only `filters.companyName` is required. The platform list defaults to all five. With no limit, every discovered job is returned.

`maxItems` is an optional positive integer limiting the combined job list across all platforms. It is allocated in the order of `platforms`. For example, if three platforms each have 600 jobs and `maxItems` is 1,000, metadata reports `600 / 600 / 600`, while the job list returns `600 / 400 / 0`. All platform metadata is still collected after the job limit is filled.

Company names match in full after Unicode normalization, case folding, punctuation removal, whitespace normalization and trailing legal-suffix removal. `ACME, Inc.` matches `Acme`; `Acme Labs` does not. All matching boards are retained; similarly named companies are never silently merged. The company is the organization publishing the board; for recruitment agencies, a job's client can differ and remains in the source description.

Discovery checks plausible ATS board addresses and queries DuckDuckGo's public HTML search. A search result is only a candidate: the company name must be confirmed by the source. Up to 20 candidates per platform are checked. Search can be blocked or incomplete, particularly for Comeet, whose company identifier cannot always be derived from its name.

**Public sources do not guarantee complete discovery.** No paid search service, source account or private ATS API key is required. No CAPTCHA solving is attempted. The Actor price is described above; post-run Dataset access or retention can still incur Apify's standard storage charges.

### Extensible platform support

Each ATS integration is implemented as an adapter with the same discovery, collection and normalization contract. New platforms can therefore be added without changing the standardized job records or platform metadata returned to existing customers.

### What can you build with this company job openings API?

- **Recruiting research:** see where a target company publishes vacancies and review all discovered openings in one table.
- **Job boards and aggregators:** export standardized records to JSON or CSV instead of maintaining five separate ATS parsers.
- **Hiring and market intelligence:** monitor hiring volume, departments and locations for companies you follow.
- **Lead generation:** identify organizations with active hiring and use verified career links in downstream enrichment workflows.
- **ATS discovery:** use platform metadata to learn which supported applicant tracking systems a company uses, even when a verified board currently has zero jobs.

The Actor searches public sources at run time. Treat the results as a current snapshot of discovered, verified boards rather than a guarantee that every page on the public web was found.

### Results

- Default Dataset: one job per row, exportable to JSON/CSV. Use `platform` to group/filter it.
- `RESULTS` in the default key-value store: `schemaVersion`, `filters` and a `platforms` array, each with `platform`, `status`, `jobs`, `errors`, `truncated`, `metadata`.
- `RUN_SUMMARY`: counts, duration, attempted/skipped boards, failures and discovery limits. Dataset and grouped JSON use the exact same records.

The fixed job contract is in [`schemas/job.schema.json`](schemas/job.schema.json):

| Fields | Meaning |
| --- | --- |
| `schemaVersion`, `recordId`, `platform`, `sourceJobId` | Schema version, stable record key, source platform and original ID |
| `companyName`, `companyWebsite`, `boardUrl` | Verified publishing organization and source board |
| `title`, `locations`, `departments` | Source job title and lists of locations/departments |
| `employmentType`, `workplaceType` | Employment and workplace information when explicit |
| `descriptionText`, `descriptionHtml` | Complete available description in original language |
| `salaryText`, `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryPeriod` | Explicit available compensation; missing information is not inferred |
| `jobUrl`, `applyUrl` | Original announcement and application links |
| `publishedAt`, `updatedAt`, `scrapedAt` | Source dates and UTC collection timestamp |

Missing scalar values are `null`; missing lists are `[]`. Source dates preserve their precision: a date-only value is not converted to an invented midnight timestamp. `updatedAt` is never substituted for publication time. Source HTML is not sanitized for embedding; sanitize it before displaying in your own application.

Record IDs derive from platform + canonical board URL + source job ID (or canonical job URL if no ID is available). Duplicates within a board/platform are removed. Cross-platform postings remain separate. Schema 1.0 fields and meanings are stable; adding a platform adds another element to the result array. Consumers should accept new platform names.

#### Statuses

| Status | Meaning |
| --- | --- |
| `success` | At least one job collected with no reported errors/limits |
| `no_jobs` | A matching board explicitly returned an empty open-jobs list |
| `not_found` | No matching board was found; this does not prove the company has no jobs |
| `partial` | Useful results or a verified empty board exist, but errors or limits prevent a complete claim |
| `blocked` | Requests were blocked and no successful board was processed |
| `error` | Collection/discovery failed and no successful board was processed |

Search blocking can make a platform `partial` even when its supplied board was completely collected. Inspect the per-board summary to distinguish discovery limitations from extraction errors. Boards after the shared optional limit is reached are `not_attempted` in the summary. If all platforms fail or are blocked, the Actor saves diagnostics and marks the run failed. A valid search with no match can succeed with zero rows.

#### Platform metadata

Each platform also returns `metadata` in RESULTS and RUN\_SUMMARY:

| Field | Meaning |
| --- | --- |
| `companyPresence` | `found`: company verified on a source board; `not_found`: no matching board found in the completed search; `unknown`: search/check failed or was blocked. `not_found` is not proof of absence from the entire platform. |
| `careerUrls` | All verified matching board URLs, including boards with zero open jobs |
| `totalJobs` | Unique open listings on the verified boards, independent of the optional global result limit; `null` if the count cannot be established |
| `returnedJobs` | Jobs actually returned in this run |
| `boards` | Per-board `companyName`, `boardUrl`, `totalJobs`; multiple same-name organizations remain separate |
| `countScope` | `verified_boards`: counts describe discovered, verified boards, not a guarantee of exhaustive platform coverage |
| `discoveryLimited` | Search failed, was blocked, or reached its candidate limit |

A known empty board has `companyPresence: "found"` and `totalJobs: 0`. An unavailable source has an unknown count (`null`), never an invented zero. A failed job-detail request does not erase a successfully established listing count. Listing counts are snapshots; a job can close between listing and detail requests.

The Actor enumerates listing pages for metadata even after the optional global detail limit is reached. This adds listing requests, but does not download every job's details. If a board count fails, its known presence/link is retained. Grouped results and summary use schema version **1.2**; the job-row contract stays **1.0**, with all 24 fields unchanged. Earlier saved result artifacts are historical and may not contain metadata or the current global-limit semantics.

### Supported source surfaces

#### Greenhouse jobs scraper

Uses the public Greenhouse Job Board API, including the available job content. The Actor verifies the company identity before adding the board and standardizes every job into the shared output schema.

#### BambooHR jobs scraper

Reads public BambooHR careers listings and job-detail pages. Company identity comes from public page data, so a guessed subdomain is never accepted as proof of a match.

#### Comeet jobs scraper

Reads public hosted Comeet job pages and their embedded company and position data. Employee-only positions are excluded, and no private Comeet API token is required.

#### JazzHR jobs scraper

Collects public `*.applytojob.com/apply` boards using structured job data with an HTML fallback. The same standardized fields are returned as for every other supported ATS.

#### Jobvite jobs scraper

Collects public `jobs.jobvite.com/{company}/jobs` boards, follows category expansion and pagination links, and uses structured data with an HTML fallback. An unsupported legacy or custom layout is reported as an extraction error instead of a false empty result.

Browser rendering is attempted for unrecognized dynamic HTML boards/details. It only reads public pages, uses the same request limits and does not submit applications. Arbitrary custom ATS layouts are not guaranteed. Public boards may change during a run.

### Run the Actor through the API

Replace `YOUR_APIFY_TOKEN` with your Apify API token. The synchronous endpoint below waits for the run and returns Dataset rows directly.

#### cURL

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/samvelkarapetyan~company-jobs-scraper/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"filters":{"companyName":"Webflow"},"platforms":["greenhouse","bamboohr","comeet","jazzhr","jobvite"]}'
```

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("samvelkarapetyan/company-jobs-scraper").call(
    run_input={
        "filters": {"companyName": "Webflow"},
        "platforms": ["greenhouse", "bamboohr", "comeet", "jazzhr", "jobvite"],
    }
)

jobs = list(client.dataset(run["defaultDatasetId"]).iterate_items())
print(f"Collected {len(jobs)} jobs")
```

#### JavaScript

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });
const run = await client.actor('samvelkarapetyan/company-jobs-scraper').call({
    filters: { companyName: 'Webflow' },
    platforms: ['greenhouse', 'bamboohr', 'comeet', 'jazzhr', 'jobvite'],
});

const { items: jobs } = await client.dataset(run.defaultDatasetId).listItems();
console.log(`Collected ${jobs.length} jobs`);
```

### Frequently asked questions

#### Do I need to know the company's careers URL or ATS?

No. Provide `filters.companyName`; the Actor searches the selected platforms, verifies matching public boards and returns discovered links in `metadata.careerUrls` and `metadata.boards`.

#### Can I use this as a career page scraper?

Yes. It discovers supported ATS career pages by company name and returns both board metadata and normalized jobs. A custom career site outside the supported ATS platforms is not collected in this version.

#### Does the Actor guarantee every job on the internet?

No. It reports every job found on the verified boards it discovers, but free public search can be blocked or incomplete. Check `status`, `errors`, `countScope` and `discoveryLimited` before making completeness claims.

#### What happens when a company has no open jobs?

A verified empty board is returned as `companyPresence: "found"`, `totalJobs: 0` and status `no_jobs`. This is different from `not_found`, which means that no matching board was discovered.

#### Can I limit the number of returned jobs?

Yes. Set the optional global `maxItems`. Platform metadata still reports the full listing counts found on verified boards, while the Dataset contains at most the requested number of job rows.

#### Which ATS platform will be supported next?

Lever, Ashby, Workday, SmartRecruiters and Workable are candidates for future adapters. [Open an Issue](https://apify.com/samvelkarapetyan/company-jobs-scraper/issues) to request a platform or describe an integration you need.

### Changelog

- **0.3 — September 2026:** name-only input, global optional result limit, verified platform metadata, stable cross-platform output and new Store presentation.
- Future releases will add and improve ATS adapters while preserving the existing standardized job and metadata contracts. Incompatible changes require a new `schemaVersion`.

### Local use

Python 3.12:

```powershell
python -m venv .venv
.venv/Scripts/python -m pip install -r requirements-dev.txt
.venv/Scripts/python -m playwright install chromium
.venv/Scripts/python -m pytest -q
```

Place input JSON at `storage/key_value_stores/default/INPUT.json`, then run `python -m src`. Use fresh local storage for each run; existing local Dataset rows can otherwise remain.

```sh
docker build -t company-jobs-scraper .
docker run --rm -v "<absolute-storage-path>:/usr/src/app/storage" company-jobs-scraper
```

The Docker image installs Chromium and its system dependencies. Start with 1 GB memory and a 30-minute timeout when deploying privately, then tune from actual runs. Large descriptions, many boards or browser fallback can require more memory/time. Maximum jobs is not a spending cap.

Scheduling, application submission and historical new/closed-job tracking are not part of this version. See [`DEVELOPMENT.md`](DEVELOPMENT.md) and [`RELEASE.md`](RELEASE.md) for extension instructions and verification evidence.

# Actor input Schema

## `filters` (type: `object`):

Required companyName. Exact match ignoring case, punctuation and legal suffixes.

## `platforms` (type: `array`):

ATS platforms to search in this run.

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

Optional maximum across all platforms. Jobs are allocated in the selected platform order. Omit to return every discovered job.

## Actor input object example

```json
{
  "filters": {
    "companyName": "Webflow"
  },
  "platforms": [
    "greenhouse",
    "bamboohr",
    "comeet",
    "jazzhr",
    "jobvite"
  ]
}
```

# Actor output Schema

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

No description

## `grouped` (type: `string`):

No description

## `summary` (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 = {
    "filters": {
        "companyName": "Webflow"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("samvelkarapetyan/company-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 = { "filters": { "companyName": "Webflow" } }

# Run the Actor and wait for it to finish
run = client.actor("samvelkarapetyan/company-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 '{
  "filters": {
    "companyName": "Webflow"
  }
}' |
apify call samvelkarapetyan/company-jobs-scraper --silent --output-dataset

```

## MCP server setup

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