# Company Jobs API - universal ATS careers scraper (`alxlin/company-jobs-api`) Actor

Company domain or careers URL -> normalized open job postings JSON. Auto-detects Greenhouse, Lever, Ashby, SmartRecruiters, Workday, Teamtailor, Personio, Recruitee, Workable and BambooHR boards. HTTP-only, no login, no personal data. Pay per job.

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

## Pricing

from $0.80 / 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.
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?

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

Turn any company's job board into clean JSON. Give it a domain (`stripe.com`), a careers page, or a direct board
URL, and it auto-detects the applicant tracking system (ATS) behind it — Greenhouse, Lever, Ashby, SmartRecruiters,
Workday, Teamtailor, Personio, Recruitee, Workable, or BambooHR — and returns every open role as one normalized
schema. No login, no browser, no anti-bot workarounds: every one of these is a public JSON (or XML) API. Built for
recruiting/sourcing pipelines, sales and investor prospecting ("who is Figma hiring for?"), labor-market research,
and AI agents that need a `list_open_roles(company)` tool.

### Sample output

One item per job, always the same shape regardless of which ATS it came from:

```json
{
  "company": "linear",
  "ats": "ashby",
  "board_url": "https://jobs.ashbyhq.com/linear",
  "job_id": "cd5ae036-0223-427a-b038-ba16ef9dcb32",
  "title": "Senior / Staff Fullstack Engineer",
  "url": "https://jobs.ashbyhq.com/linear/cd5ae036-0223-427a-b038-ba16ef9dcb32",
  "location": "North America",
  "locations": ["North America"],
  "remote": true,
  "department": "Product",
  "team": "Engineering",
  "employment_type": "FullTime",
  "posted_at": "2021-08-18T20:48:26.891Z",
  "updated_at": null,
  "salary_min": null,
  "salary_max": null,
  "salary_currency": null,
  "scraped_at": "2026-09-13T19:46:20.424Z"
}
```

Set `include_description: true` to add `description_text`/`description_html`, and `include_raw: true` to attach
the original ATS record (with recruiter names/emails stripped) as `raw`. Download the dataset as JSON, CSV, Excel,
or query it over the API.

### Why this one

The Store has a dozen single-ATS "Greenhouse jobs" / "Lever jobs" / "Workday jobs" actors, each covering one
system. This one covers all ten from a single input, so you don't need to know in advance what a company runs —
useful for sourcing lists that span hundreds of companies. It's plain `fetch` against public JSON/XML endpoints:
no browser, no proxy required, no personal data (contact emails and recruiter names are stripped from every
output field, including `raw`).

### How it works

1. Add company domains, careers-page URLs, and/or (if you already know them) direct board URLs to the input.
2. For each `companies` entry, the Actor tries, in order: (a) does the string already look like one of the 10
   ATS's own URLs, (b) guess the board slug from the domain and probe each ATS's public API, (c) fetch the
   company's careers page and look for an embedded Greenhouse/Lever/Ashby/etc. link. First hit wins.
3. Every job on the resolved board is fetched, normalized, optionally filtered, and pushed to the dataset.

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `companies` | string\[] | `["stripe.com", "figma.com"]`\* | Domains or careers-page URLs. ATS is auto-detected. |
| `boards` | string\[] | `["https://jobs.lever.co/spotify", "https://jobs.ashbyhq.com/linear"]`\* | Direct board/API URLs — skips detection. |
| `include_description` | boolean | `false` | Adds `description_text`/`description_html`. Costs one extra request per job for Workday/SmartRecruiters/BambooHR/Workable. |
| `posted_after` | string (ISO date) | — | Keeps jobs with no known date. |
| `keyword` | string | — | Matches title/department/team/description. `\|`-separated alternatives. |
| `location` | string | — | Substring match against the job's location(s). |
| `max_jobs_per_company` | integer | `500` | Cap per resolved board, applied after filtering. |
| `max_total_jobs` | integer | unlimited | Cap for the whole run. |
| `include_raw` | boolean | `false` | Attaches the source ATS record (PII stripped) as `raw`. |

\* The defaults only apply when **both** `companies` and `boards` are left empty — this is what a zero-config
run uses, so it always returns real jobs from four different ATSs in well under a minute.

### Supported ATSs

Greenhouse · Lever · Ashby · SmartRecruiters · Workday · Teamtailor · Personio · Recruitee · Workable · BambooHR —
all ten verified end to end (list + normalize) against live company boards while building this Actor. If a
company's board is on one of these ten but detection fails (some careers pages hide the board behind
JavaScript), pass the direct board URL in `boards` instead.

### Pricing

Pay per event, no subscription:

| Event | Price | When |
|---|---|---|
| Actor start | $0.002 | Once per run |
| Board resolved | $0.005 | Once per company/board where an ATS was successfully found |
| Job | $0.001 | Once per normalized job written to the dataset (primary event) |

Worked examples:

- **100 jobs from one company**: $0.002 + $0.005 + 100 × $0.001 = **$0.107**
- **The default sample run** (Stripe, Figma, Spotify, Linear — 755 jobs across 4 boards): $0.002 + 4 × $0.005 +
  755 × $0.001 = **$0.777**
- **Free plan**: the $5/month free usage plus the platform's own $0.20/GB-hour compute more than covers
  occasional runs — this Actor is HTTP-only and typically finishes a few hundred jobs in under a minute.

### Limits & notes

- Public data only: open job postings as published by the company. No login, no candidate data.
- Recruiter/HR contact emails are stripped from every text field and from `raw`.
- Some ATSs (Workday tenant/site, SmartRecruiters company id) aren't reliably guessable from a bare domain if the
  company uses a non-obvious slug — pass the board URL directly via `boards` in that case.
- Not affiliated with Greenhouse, Lever, Ashby, SmartRecruiters, Workday, Teamtailor, Personio, Recruitee,
  Workable, or BambooHR. Trademarks belong to their respective owners.
- Found a company whose board doesn't resolve, or a parsing bug? Open an issue on this Actor's Issues tab.

# Actor input Schema

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

Company domains (stripe.com) or careers-page URLs (https://www.figma.com/careers). The Actor finds the company's ATS job board automatically (Greenhouse, Lever, Ashby, SmartRecruiters, Workday, Teamtailor, Personio, Recruitee, Workable, BambooHR).

## `boards` (type: `array`):

Direct board or API URLs when you already know them, e.g. https://jobs.lever.co/spotify, https://jobs.ashbyhq.com/linear, https://adobe.wd5.myworkdayjobs.com/external\_experienced, https://bunq.recruitee.com. Skips detection.

## `include_description` (type: `boolean`):

Add description\_text and description\_html to every job. Larger output; for Workday/SmartRecruiters this costs one extra request per job.

## `posted_after` (type: `string`):

Only jobs posted (or, if the ATS has no posted date, last updated) on/after this ISO date, e.g. 2026-09-01. Jobs with no date are kept.

## `keyword` (type: `string`):

Case-insensitive match against title, department and team (and description when included). Separate alternatives with | e.g. "engineer|developer".

## `location` (type: `string`):

Case-insensitive substring match against the job's locations, e.g. "London" or "remote".

## `max_jobs_per_company` (type: `integer`):

Stop after this many jobs from one board (after filtering).

## `max_total_jobs` (type: `integer`):

Stop the whole run after this many jobs. Leave empty for no limit (your per-run spending limit still applies).

## `include_raw` (type: `boolean`):

Attach the original ATS JSON (minus personal-data fields) as `raw` on every job.

## Actor input object example

```json
{
  "companies": [
    "stripe.com",
    "figma.com"
  ],
  "boards": [
    "https://jobs.lever.co/spotify",
    "https://jobs.ashbyhq.com/linear"
  ],
  "include_description": false,
  "max_jobs_per_company": 50,
  "include_raw": false
}
```

# Actor output Schema

## `jobs` (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": [
        "stripe.com",
        "figma.com"
    ],
    "boards": [
        "https://jobs.lever.co/spotify",
        "https://jobs.ashbyhq.com/linear"
    ],
    "max_jobs_per_company": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("alxlin/company-jobs-api").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",
        "figma.com",
    ],
    "boards": [
        "https://jobs.lever.co/spotify",
        "https://jobs.ashbyhq.com/linear",
    ],
    "max_jobs_per_company": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("alxlin/company-jobs-api").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",
    "figma.com"
  ],
  "boards": [
    "https://jobs.lever.co/spotify",
    "https://jobs.ashbyhq.com/linear"
  ],
  "max_jobs_per_company": 50
}' |
apify call alxlin/company-jobs-api --silent --output-dataset

```

## MCP server setup

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

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/cjbITdAmbGOuf9sfN/builds/rjmcVBrxbjHcmKMIX/openapi.json
