# Company Careers Jobs — Straight from Greenhouse, Lever & Ashby (`atomo-studio/company-careers-jobs`) Actor

Pull every open role straight from a company's own job board API. Give it company names - it detects Greenhouse, Lever or Ashby and returns all postings in one schema. Official public endpoints: nothing blocked or rate-limited, and jobs land here before aggregators index them.

- **URL**: https://apify.com/atomo-studio/company-careers-jobs.md
- **Developed by:** [Atomo Studio](https://apify.com/atomo-studio) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 job postings

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Company Careers Jobs — 11 ATS Platforms, One Schema

Most job scrapers fight Indeed and LinkedIn for data those sites actively defend.
This one doesn't fight anything.

Eleven ATS platforms — **Greenhouse, Lever, Ashby, Workday, SmartRecruiters, Workable,
Teamtailor, Recruitee, Personio, Breezy and Rippling** — publish every company's open
roles through **public endpoints that exist to be read**. That is how a company's own careers page renders its
listings. No authentication, no rate limits, nothing to block.

Most job-board Actors cover three of them. Covering all eleven matters because those
three are dominated by US startups, while the rest are where European scale-ups
(Workable, Teamtailor, Recruitee, Personio) and large enterprises (Workday,
SmartRecruiters) live. **Bosch alone lists over 4,800 roles on SmartRecruiters, and
Salesforce over 1,400 on Workday.**

Measured against 66 well-known employers: **three platforms found 76%, eleven found 89%.**

And it is upstream: a role appears on the company's own board the moment it opens,
often days before an aggregator indexes it.

**You give it company names. It works out which ATS each one uses.**

```
stripe      → Greenhouse        615 jobs
openai      → Ashby             778 jobs
palantir    → Lever             311 jobs
BoschGroup  → SmartRecruiters  4,812 jobs
adobe       → Workday            723 jobs
nike        → Workday            742 jobs
zego        → Workable            36 jobs
doktor      → Teamtailor          46 jobs
channable   → Recruitee           13 jobs
rippling    → Rippling           683 jobs
```

Workday needs three pieces to address a board (tenant, server number, site name), so a
company it cannot resolve by name can be given as a board URL instead —
`https://cloudera.wd5.myworkdayjobs.com/External_Career` — and it is read directly.

`stripe`, `Stripe`, `stripe.com` and `https://stripe.com/jobs` all resolve to the same
board. In testing, 33 of 38 well-known tech companies (86%) were detected automatically.

### What you get

One record per open role, in a single schema across all three systems:

```json
{
  "company": "stripe",
  "ats": "greenhouse",
  "job_id": "8172487",
  "title": "Abuse Investigator",
  "department": "Security Analytics",
  "location": "Dublin",
  "locations": ["Ireland Locations"],
  "is_remote": false,
  "workplace_type": "onsite",
  "posted_at": "2026-09-03T13:30:34-04:00",
  "updated_at": "2026-09-03T13:30:34-04:00",
  "url": "https://stripe.com/jobs/search?gh_jid=8172487",
  "apply_url": "https://stripe.com/jobs/search?gh_jid=8172487",
  "description": "Who we are\n\nAbout Stripe\n\nStripe is a financial infrastructure...",
  "description_chars": 4596,
  "source": "Company career site (public job board API)"
}
```

| Group | Fields |
|---|---|
| **Role** | `title`, `department`, `team`, `employment_type`, `experience_level`, `industry`, `compensation` |
| **Where** | `location`, `locations`, `country`, `is_remote`, `workplace_type` |
| **When** | `posted_at`, `updated_at` |
| **Links** | `url`, `apply_url` |
| **Text** | `description` (HTML converted to plain text), `description_chars` |
| **Identity** | `company`, `company_slug`, `ats`, `job_id` |

Three details the raw APIs get wrong, and this Actor fixes:

- **Remote status.** Greenhouse does not publish a remote flag at all; Workable gives a
  clean `telecommuting` boolean; Lever and Ashby disagree on how to express one. Here every role gets `is_remote` and a
  `workplace_type` of `remote` / `hybrid` / `onsite`, inferred from the location text
  where the upstream field is missing.
- **Dates.** Lever returns millisecond epochs, the others ISO strings. All become ISO 8601.
- **Descriptions.** Greenhouse double-escapes its HTML (`&lt;div&gt;`). Unescaped,
  stripped and normalised to readable text.

### Input

| Field | Type | Notes |
|---|---|---|
| `companies` | array | Names, slugs or career URLs. Empty = a sample of well-known tech companies |
| `includeDescription` | boolean | Full posting text. Default on |
| `titleKeywords` | array | Keep roles matching a title term |
| `locationKeywords` | array | Keep roles matching a location |
| `departments` | array | Keep roles in matching departments or teams |
| `remoteOnly` | boolean | Fully remote roles only |
| `postedWithinDays` | integer | Only roles first published in the last N days |
| `maxItems` | integer | Cap the run. `0` = no limit |

Results are **interleaved across companies**, so a capped run still covers every
company you asked for rather than exhausting the first one.

Companies whose board cannot be found are reported by name in the log rather than
dropped silently — they either use a different ATS (Workday, SmartRecruiters, Taleo)
or their board token differs from their company name.

### Who this is for

- **Job boards and aggregators** wanting a clean, fast, unblockable upstream feed
- **Recruiters and sourcers** tracking who is hiring for what, before it hits LinkedIn
- **Market and competitive analysts** — headcount direction, which teams a rival is
  building, which cities they are opening
- **Job seekers** monitoring a shortlist of companies
- **AI agents** needing structured, current hiring data

### Notes

**Official endpoints, not scraping.** Every request goes to a documented public job
board API that the ATS vendors provide for exactly this purpose. The Actor never
touches a rendered careers page, so redesigns and bot defences do not break it.

**No personal data.** Job postings occasionally carry a recruiter's email or phone in
the body text. Those are stripped before the record is emitted.

**Coverage.** The seven platforms together cover venture-backed tech (Greenhouse, Lever,
Ashby, Rippling), European scale-ups (Workable, Teamtailor) and large enterprises
(SmartRecruiters). Workday and Taleo do not expose a comparable public board, so those
companies show up in the "not found" log line rather than being dropped silently.

***

### Development

```bash
apify push
```

ATS 응답 모양이 바뀌면 `src/normalize.py`의 해당 핸들러만 고치면 된다.
탐지 순서는 `src/ats.py` — Greenhouse가 가장 흔해서 먼저 물어본다.

# Actor input Schema

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

Company names, slugs or career-page URLs - 'stripe', 'Stripe', 'stripe.com' and 'https://stripe.com/jobs' all work. The Actor works out which of 11 ATS platforms each one uses. For a Workday employer it cannot resolve by name, paste its Workday board URL (e.g. https://cloudera.wd5.myworkdayjobs.com/External\_Career) and it is read directly.

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

Return the complete posting text, converted from HTML to plain text. Turn off for a lighter, faster dataset.

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

Keep only roles whose title contains one of these, e.g. engineer, designer, staff.

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

Keep only roles matching a location, e.g. London, Germany, New York.

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

Keep only roles in matching departments, e.g. engineering, sales, design.

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

Keep only fully remote roles. Greenhouse does not publish a remote flag, so it is inferred from the location text.

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

Keep only roles first published in the last N days. 0 = no limit.

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

Stop after this many postings. Results are interleaved across companies, so a capped run still covers every company. 0 = no limit.

## Actor input object example

```json
{
  "companies": [
    "stripe",
    "openai",
    "BoschGroup",
    "adobe",
    "nike",
    "doktor",
    "zego",
    "rippling"
  ],
  "includeDescription": true,
  "titleKeywords": [],
  "locationKeywords": [],
  "departments": [],
  "remoteOnly": false,
  "postedWithinDays": 0,
  "maxItems": 300
}
```

# Actor output Schema

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

One record per open role - title, department, location, remote flag, posted date, full description and apply link.

## `downloadCsv` (type: `string`):

The same records as a spreadsheet.

# 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",
        "openai",
        "BoschGroup",
        "adobe",
        "nike",
        "doktor",
        "zego",
        "rippling"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("atomo-studio/company-careers-jobs").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",
        "openai",
        "BoschGroup",
        "adobe",
        "nike",
        "doktor",
        "zego",
        "rippling",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("atomo-studio/company-careers-jobs").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",
    "openai",
    "BoschGroup",
    "adobe",
    "nike",
    "doktor",
    "zego",
    "rippling"
  ]
}' |
apify call atomo-studio/company-careers-jobs --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,atomo-studio/company-careers-jobs"
        }
    }
}

```

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/8xSYNoL1N8DAE9mo9/builds/ECU1GMJy5OkOXKGJn/openapi.json
