# ATS Career-Site Jobs (Greenhouse, Lever, Ashby) (`skilled_glee/ats-career-site-jobs`) Actor

Get open jobs from company career sites hosted on Greenhouse, Lever or Ashby as one normalized JSON schema: title, department, location, remote flag, posted date, apply URL. Uses the public job-board APIs. No login, no browser, no proxy.

- **URL**: https://apify.com/skilled_glee/ats-career-site-jobs.md
- **Developed by:** [EchoEmpire Machine Foundry (EchoEmpire LLC)](https://apify.com/skilled_glee) (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

$7.88 / 1,000 job extracteds

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

## ATS Career-Site Jobs (Greenhouse, Lever, Ashby)

Company career-site slugs in, open jobs out, in one JSON schema no matter which ATS hosts the board.

### Which jobs Actor should I use?

Use this one when you already know **which companies** you care about and they host their career site on **Greenhouse, Lever or Ashby**. It reads those boards' public job-board APIs and normalizes every posting into the same fields. It does **not** cover other ATS vendors (Workday, iCIMS, SmartRecruiters, etc.) or job aggregators. For keyword search across LinkedIn, use [LinkedIn Jobs](https://apify.com/skilled_glee/linkedin-jobs).

| | |
|---|---|
| Input | `companies[]` (board slugs or board URLs), optional title/location/remote/date filters |
| Output per job | `id`, `title`, `company`, `companySlug`, `sourceAts`, `department`, `team`, `location`, `remote`, `workplaceType`, `employmentType`, `postedAt`, `updatedAt`, `url`, `applyUrl`, `status` |
| Pricing | $0.00788 per job returned ($7.88 / 1,000). Failed boards are not billed. |
| Access | Public job-board APIs (`boards-api.greenhouse.io`, `api.lever.co`, `api.ashbyhq.com`). No login, no browser, no proxy. |
| Call it | REST `skilled_glee~ats-career-site-jobs` · MCP `https://mcp.apify.com/?tools=skilled_glee/ats-career-site-jobs` |

Pipeline role (n8n / agents): **Jobs → Discover + Fetch**. Next step for hiring leads: pass company sites to [Contact Details Extractor](https://apify.com/skilled_glee/contact-details-extractor). Full catalog of steps: [EchoEmpire Machine Foundry llms.txt](https://foundry.echoempire.org/llms.txt).

### Input

```json
{
  "companies": ["greenhouse:stripe", "https://jobs.lever.co/palantir", "ashby:ramp"],
  "titleKeywords": ["engineer"],
  "maxJobsPerCompany": 3
}
```

- `companies` (required, 1–100): `greenhouse:SLUG`, `lever:SLUG`, `ashby:SLUG`, or a board URL (`boards.greenhouse.io/SLUG`, `job-boards.greenhouse.io/SLUG`, `jobs.lever.co/SLUG`, `jobs.ashbyhq.com/SLUG`). The slug is the company part of the board URL.
- `ats` (default `auto`): used for bare slugs. `auto` tries Greenhouse, then Lever, then Ashby and uses the first board that exists. The same slug can belong to different companies on different systems, so prefix the slug when in doubt.
- `titleKeywords`, `locationKeywords` (optional): keep jobs whose title / location contains any of the words (case-insensitive).
- `remoteOnly` (default `false`), `postedWithinDays` (default `0` = off).
- `maxJobsPerCompany` (1–2000, default 200): limit after filters.
- `includeDescription` (default `false`): adds `descriptionText` (plain text).

### Output

One dataset row per job. Rows from a real run (2026-10-29):

```json
{"id": "8172503", "title": "Abuse Research Engineer", "company": "Stripe", "companySlug": "stripe", "sourceAts": "greenhouse", "department": "8611 Security Analytics", "team": null, "location": "Remote from the US", "remote": true, "workplaceType": null, "employmentType": null, "postedAt": "2026-09-09T14:52:09Z", "updatedAt": "2026-10-29T14:42:09Z", "url": "https://stripe.com/jobs/search?gh_jid=8172503", "applyUrl": "https://stripe.com/jobs/search?gh_jid=8172503", "input": "greenhouse:stripe", "status": "ok"}
```

Field notes:

- `postedAt`: Greenhouse `first_published`, Lever `createdAt`, Ashby `publishedAt`, as UTC ISO 8601. `updatedAt` is only available on Greenhouse.
- `remote`: `true` when the ATS marks the job remote (Ashby `isRemote`, Lever `workplaceType: remote`) or the location text contains "remote"; `false` only when Lever says on-site; otherwise `null` (unknown). It is not a guess.
- `team`, `workplaceType`, `employmentType` come from Lever/Ashby; Greenhouse's public API does not expose them, so they are `null` there.
- `company` is the company name from Greenhouse; for Lever and Ashby (whose APIs do not return a name) it is the slug.

A company that fails (unknown slug, not one of the three ATS, HTTP error) produces one row `{"input": "...", "status": "error", "error": "..."}` and is not billed. A run summary (per-company counts, jobs charged, whether a spending limit stopped the run) is saved as `SUMMARY` in the default key-value store.

### Pricing (pay per event)

| Event | When | Price |
|---|---|---|
| `job-extracted` | Once per job row with `status: "ok"` saved to the dataset | $0.00788 |

No start fee. Error rows are free. Example: 3 companies × 50 jobs = 150 × $0.00788 = $1.18. If you set a maximum charge per run, the Actor stops saving jobs when the limit is reached.

### Limits

- Greenhouse, Lever and Ashby public boards only; boards a company hides or keeps internal are not visible.
- Only currently open postings (these APIs do not return closed jobs).
- No salary/compensation fields.

# Actor input Schema

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

1-100 entries. Use 'greenhouse:SLUG', 'lever:SLUG', 'ashby:SLUG', or a board URL such as https://boards.greenhouse.io/stripe, https://jobs.lever.co/palantir, https://jobs.ashbyhq.com/ramp. A bare slug is tried on Greenhouse, then Lever, then Ashby (first hit wins), so prefix it when a slug may exist on several systems.

## `ats` (type: `string`):

Which system to use for entries without a prefix or URL. 'auto' tries Greenhouse, Lever, Ashby in that order.

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

Keep only jobs whose title contains at least one of these words (case-insensitive). Empty = all jobs.

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

Keep only jobs whose location text contains at least one of these (case-insensitive), e.g. 'New York', 'Remote', 'Germany'.

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

Keep only jobs with remote = true (from the ATS remote/workplace field, or the word 'remote' in the location).

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

Keep only jobs posted in the last N days. 0 = no date filter. Jobs without a posted date are dropped when this is set.

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

Upper limit of job rows per company after filters (1-2000). Billing is per job row returned.

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

Add 'descriptionText' (plain text of the job ad). Off by default to keep rows small.

## Actor input object example

```json
{
  "companies": [
    "greenhouse:stripe",
    "https://jobs.lever.co/palantir",
    "ashby:ramp"
  ],
  "ats": "auto",
  "remoteOnly": false,
  "postedWithinDays": 0,
  "maxJobsPerCompany": 200,
  "includeDescription": false
}
```

# Actor output Schema

## `jobs` (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 = {
    "companies": [
        "greenhouse:stripe",
        "https://jobs.lever.co/palantir",
        "ashby:ramp"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("skilled_glee/ats-career-site-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": [
        "greenhouse:stripe",
        "https://jobs.lever.co/palantir",
        "ashby:ramp",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("skilled_glee/ats-career-site-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": [
    "greenhouse:stripe",
    "https://jobs.lever.co/palantir",
    "ashby:ramp"
  ]
}' |
apify call skilled_glee/ats-career-site-jobs --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,skilled_glee/ats-career-site-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/HdN66GyEXYth3pNvj/builds/a4bQBCueUoESnchZf/openapi.json
