# Career Site Jobs API (`teddythebuilder/career-site-jobs-api`) Actor

Get job listings from company career sites on Greenhouse, Lever, Ashby, Workable, Recruitee and Teamtailor in one clean JSON schema. Paste career page URLs; get title, department, location, remote/hybrid, salary, dates and apply links. No login. Only-new and removed-job tracking for scheduled runs.

- **URL**: https://apify.com/teddythebuilder/career-site-jobs-api.md
- **Developed by:** [Teddy Wasserman](https://apify.com/teddythebuilder) (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 $1.10 / 1,000 jobs

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

### What does Career Site Jobs API do?

**Career Site Jobs API** pulls the open job listings straight from company career sites. It covers six applicant tracking systems (ATS): **Greenhouse, Lever, Ashby, Workable, Recruitee and Teamtailor**. Paste career page URLs such as `https://boards.greenhouse.io/gitlab` or `https://tibber.teamtailor.com`. You get one clean, identical JSON schema for every company: job title, department, team, locations, country, remote/hybrid/on-site, employment type, salary range (when the employer publishes it), posting dates, job page URL and apply URL.

It's built for recruiters, sales teams, job boards, labour-market analysts and AI agents who need **fresh, first-party job data**. The data comes directly from the employer's own ATS, before it reaches LinkedIn or Indeed. Schedule it daily with **only-new mode**, and it outputs just the jobs that appeared since the last run. Optionally it also outputs the jobs that were **removed** (filled or closed).

### Why use Career Site Jobs API?

- **Six ATS platforms, one schema.** Greenhouse, Lever, Ashby, Workable, Recruitee and Teamtailor all map to the same fields, so you don't need per-vendor parsing.
- **Teamtailor support for the Nordics.** Teamtailor is the leading ATS in Sweden, Norway, Denmark and Finland, and it's rarely covered by other job APIs. Custom career domains like `jobs.tibber.com` are detected automatically.
- **No login, no browser, no proxies.** The actor reads the public job-board feeds that the ATS vendors provide for embedding jobs on career sites. Runs are fast (a few seconds for a handful of companies) and cheap.
- **New and removed job tracking.** Only-new mode plus removed-job detection turns a list of companies into a hiring-signal feed.
- **Salary normalisation.** Where the employer publishes pay (common on Ashby, Lever and Recruitee), you get `salaryMin`, `salaryMax`, `salaryCurrency` and `salaryInterval`.
- **Privacy by design.** Recruiter names, mailbox addresses and contact fields are never output. E-mail addresses and phone numbers inside descriptions are removed.
- **Fair, transparent pricing.** $2 per 1,000 jobs, a tiny start fee, and automatic volume discounts on higher Apify plans.

### What data can you extract?

| Field | Example | Description |
|---|---|---|
| `jobId` | `ashby:ramp:0d73…` | Stable unique ID: `ats:company:id` |
| `status` | `active` | `active`, or `removed` when emitRemoved is on |
| `ats` | `teamtailor` | greenhouse, lever, ashby, workable, recruitee, teamtailor |
| `company` | `Tibber` | Company name from the career site (or prettified slug) |
| `companySlug` | `tibber` | The board identifier you passed in |
| `title` | `Senior Security Engineer` | Job title |
| `department` / `team` | `Customer Experience` / `CX Frontline` | As defined by the employer |
| `location` / `locations` | `Stockholm, Sweden` | Primary location and all locations |
| `country` / `countryCode` | `Sweden` / `SE` | When the ATS provides it |
| `workplaceType` | `hybrid` | remote, hybrid, onsite (or null if unknown) |
| `isRemote` | `false` | Convenience boolean |
| `employmentType` | `FULL_TIME` | FULL_TIME, PART_TIME, CONTRACT, INTERNSHIP, TEMPORARY, OTHER |
| `salaryMin` / `salaryMax` | `39000` / `43000` | Numeric pay range when published |
| `salaryCurrency` / `salaryInterval` | `GBP` / `1 YEAR` | Currency and period |
| `salaryText` | `£39K - £43K` | Human-readable salary |
| `postedAt` / `updatedAt` | `2026-10-01T18:38:56+00:00` | ISO 8601 UTC |
| `jobUrl` / `applyUrl` | `https://jobs.ashbyhq.com/…` | Job page and application link |
| `descriptionText` | `About Ramp…` | Plain-text description (optional, truncated) |
| `descriptionHtml` | `<p>…</p>` | Original HTML (optional) |
| `isNew` | `true` | In monitoring mode: first time this job is seen |
| `scrapedAt` | `2026-10-04T09:59:28+00:00` | When the run read the job |

### How to use Career Site Jobs API (step by step)

1. Click **Try for free** and open the **Input** tab.
2. Under **Career sites**, paste one career page per line. You can use the ATS board URL (`https://jobs.lever.co/spotify`), a Teamtailor custom domain (`jobs.tibber.com`), or shorthand (`greenhouse:gitlab`).
3. Optionally narrow the results with **Title keywords** (e.g. `engineer`, `sales`), **Locations** (e.g. `Sweden`, `Berlin`, `remote`), **Remote jobs only** or **Posted within last N days**.
4. Set **Max results**. Results are spread evenly across companies, so a small limit still shows every company.
5. Click **Start**. A typical run with 5–10 companies finishes in under 10 seconds.
6. Download the results as JSON, CSV, Excel or HTML, or connect them to Google Sheets, Make, Zapier, n8n or a webhook.

To get **daily new-job alerts**, turn on **Only new jobs since last run**, choose a **Monitor ID** and create a **Schedule** (for example every morning at 07:00).

#### How do I find a company's ATS URL?

Open the company's careers page and click any job. The address bar or the "Apply" button usually reveals the ATS:

- Greenhouse: `boards.greenhouse.io/<company>` or `job-boards.greenhouse.io/<company>`
- Lever: `jobs.lever.co/<company>` (EU: `jobs.eu.lever.co/<company>`)
- Ashby: `jobs.ashbyhq.com/<company>`
- Workable: `apply.workable.com/<company>`
- Recruitee: `<company>.recruitee.com`
- Teamtailor: `<company>.teamtailor.com` or a custom domain such as `jobs.<company>.com` (pass the domain as is)

### Input example

```json
{
  "companies": [
    "https://tibber.teamtailor.com",
    "https://jobs.lever.co/spotify",
    "https://boards.greenhouse.io/gitlab",
    "ashby:ramp",
    "https://apply.workable.com/huggingface",
    "https://bunq.recruitee.com"
  ],
  "keywords": ["engineer", "developer"],
  "locations": ["Sweden", "Germany", "remote"],
  "postedWithinDays": 30,
  "maxResults": 500,
  "includeDescription": true,
  "onlyNew": false
}
```

### Output example

```json
[
  {
    "jobId": "teamtailor:tibber:ef147f7e-8acb-4ad7-ad55-023ff7f93e36",
    "status": "active",
    "ats": "teamtailor",
    "company": "Tibber",
    "companySlug": "tibber",
    "title": "Senior Security Engineer",
    "department": "House of Product & Engineering",
    "team": null,
    "location": "Stockholm, Sweden",
    "locations": ["Stockholm, Sweden"],
    "country": "Sweden",
    "countryCode": null,
    "workplaceType": "hybrid",
    "isRemote": false,
    "employmentType": null,
    "salaryMin": null,
    "salaryMax": null,
    "salaryCurrency": null,
    "salaryInterval": null,
    "salaryText": null,
    "postedAt": "2026-09-11T10:54:15+00:00",
    "updatedAt": null,
    "jobUrl": "https://jobs.tibber.com/jobs/8365823-senior-security-engineer",
    "applyUrl": "https://jobs.tibber.com/jobs/8365823-senior-security-engineer",
    "descriptionText": "We're at a pivotal moment for both Tibber and the planet. When joining Tibber, you won't just help scale a forward-thinking tech company…",
    "isNew": null,
    "scrapedAt": "2026-10-04T09:59:28+00:00"
  },
  {
    "jobId": "ashby:ramp:0d731586-3626-48fb-9dc5-3061ae5fd240",
    "status": "active",
    "ats": "ashby",
    "company": "Ramp",
    "companySlug": "ramp",
    "title": "Customer Experience Associate - London",
    "department": "Customer Experience",
    "team": "CX Frontline",
    "location": "London",
    "locations": ["London"],
    "country": "United Kingdom",
    "workplaceType": "hybrid",
    "isRemote": false,
    "employmentType": "FULL_TIME",
    "salaryMin": 39000,
    "salaryMax": 43000,
    "salaryCurrency": "GBP",
    "salaryInterval": "1 YEAR",
    "salaryText": "£39K - £43K",
    "postedAt": "2026-10-01T18:38:56+00:00",
    "jobUrl": "https://jobs.ashbyhq.com/ramp/0d731586-3626-48fb-9dc5-3061ae5fd240",
    "applyUrl": "https://jobs.ashbyhq.com/ramp/0d731586-3626-48fb-9dc5-3061ae5fd240/application",
    "descriptionText": "About Ramp\n\nRamp is building the smart infrastructure for finance teams…",
    "isNew": null,
    "scrapedAt": "2026-10-04T09:59:28+00:00"
  }
]
```

A removed job (with **Also output removed jobs** on) looks like this:

```json
{
  "jobId": "lever:spotify:6ed76ce8-…",
  "status": "removed",
  "ats": "lever",
  "company": "Spotify",
  "title": "Backend Engineer",
  "location": "Stockholm, Sweden",
  "jobUrl": "https://jobs.lever.co/spotify/6ed76ce8-…",
  "removedDetectedAt": "2026-10-05T07:00:12+00:00"
}
```

The **Jobs** and **Salaries** table views in the Output tab are ready for CSV/Excel export.

### How much does it cost?

The actor uses **pay-per-event pricing**: you pay only for the jobs you receive.

| Event | Price | When it's charged |
|---|---|---|
| Actor start | $0.0005 | Once per run (per GB of memory; the default is 256 MB) |
| Job | $0.002 | Per job (or removed-job row) delivered to the dataset ($2 per 1,000) |

On higher Apify plans the job price drops automatically: **Bronze −15%, Silver −30%, Gold and above −45%**.

Worked examples (Free/Starter pricing):

- **1,000 jobs** from 50 companies, once: about **$2.00**.
- **Daily new-job alerts** for 100 companies with ~40 new jobs per day: 30 runs × (40 × $0.002 + $0.0005) ≈ **$2.42 per month**.
- **A full weekly export** of 20 companies with 3,000 open jobs: about **$6 per run**, or **$24 per month**.

Platform compute is tiny (a few seconds at 256 MB). The free Apify plan's monthly credits are enough to try the actor many times. Use **Max results** to cap any run.

### Use cases

- **Recruiters and staffing agencies.** Watch target companies and get new openings the morning they're posted, before they reach the big job boards.
- **B2B sales and account-based marketing.** Hiring is a buying signal: a company hiring "Data Engineers" or "Head of Security" is likely to buy related tools. Feed new jobs into your CRM.
- **Job boards and niche job sites.** Aggregate first-party listings from hundreds of employers in a single schema, with stable IDs and removal detection to keep your board clean.
- **Labour-market and competitor intelligence.** Track how many roles competitors open by department, location and seniority, and how salary ranges change.
- **Investors and analysts.** Hiring velocity at portfolio or target companies is an early growth indicator.
- **AI agents and copilots.** A compact, typed job object per row, which works well for agents that search jobs, summarise hiring trends or match candidates.

### Use with AI agents, MCP and the API

The actor works with the **Apify MCP server**, so assistants like Claude or ChatGPT can call it as a tool. The first sentence of this README and the small input schema are written for that. The start fee is a fraction of a cent, so agent probe calls stay cheap.

Call it from any language with the Apify API:

```bash
curl -X POST "https://api.apify.com/v2/acts/teddythebuilder~career-site-jobs-api/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"companies": ["https://jobs.lever.co/spotify", "greenhouse:gitlab"], "keywords": ["engineer"], "maxResults": 50}'
```

Or with the Python client:

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_TOKEN")
run = client.actor("teddythebuilder/career-site-jobs-api").call(run_input={
    "companies": ["https://tibber.teamtailor.com", "https://jobs.ashbyhq.com/ramp"],
    "locations": ["Sweden", "remote"],
    "onlyNew": True,
    "monitorId": "nordic-tech",
})
for job in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(job["company"], job["title"], job["jobUrl"])
```

### Integrations

- **Google Sheets**: export each run to a sheet, or append new jobs daily.
- **Make, Zapier and n8n**: trigger a scenario on run success and post new jobs to Slack, Teams, HubSpot, Pipedrive or Airtable.
- **Webhooks**: receive a POST when a run finishes and fetch the dataset items.
- **Schedules**: run hourly, daily or weekly with a fixed Monitor ID for a continuous new-job feed.

### Data source and legality

The actor only reads **public job-board endpoints that the ATS vendors offer so companies can show their open positions on their own websites and job aggregators**:

- Greenhouse Job Board API
- Lever Postings API
- Ashby Posting API
- Workable job widget API
- Recruitee careers API
- Teamtailor career-site RSS feed

It doesn't log in, doesn't bypass any protection, respects robots.txt, and sends only a few polite requests per company. SmartRecruiters isn't supported, because its API's robots.txt disallows generic automated agents.

Job postings are published by the employers so that people can see and apply for them. **No personal data is collected.** Recruiter names, contact persons, e-mail addresses and phone numbers are dropped or redacted. You're responsible for using the data in line with your local laws and the employers' terms, for example by linking back to the original `jobUrl`.

### FAQ

**Which companies can I use?** Any company whose career site runs on Greenhouse, Lever, Ashby, Workable, Recruitee or Teamtailor. That's tens of thousands of companies, from startups to large enterprises.

**How fresh is the data?** Live. Every run reads the current state of each career site, so new jobs appear as soon as the employer publishes them.

**What happens if one career site fails?** The run continues with the others, and failed sites are listed in the run's status message and log. The run only fails if every site fails, usually because the URLs are wrong.

**Why is `salaryMin` empty?** Most employers don't publish pay. Salary fields are filled when the ATS exposes it, most often on Ashby, Lever and Recruitee boards, especially for US, UK and EU pay-transparency locations.

**Why is `employmentType` null for Teamtailor jobs?** Teamtailor's public feed doesn't include it. All other fields are present.

**How does only-new mode work?** The actor stores the IDs of the jobs it has delivered in a named key-value store, per **Monitor ID**. Later runs skip those jobs. Use different Monitor IDs for different company lists.

**Can I use it on the free Apify plan?** Yes. A typical run costs well under one cent of platform usage, plus the per-job price above.

**I need another ATS or an extra field.** Open an issue on the **Issues** tab and we usually reply within 24 hours. Workday and other ATS platforms are on the roadmap. If the actor saves you time, a short review helps other users find it.

### Related actors

- [Swedish Jobs Scraper (Platsbanken API)](https://apify.com/teddythebuilder/swedish-jobs-monitor): every job ad in Sweden from Arbetsförmedlingen's official API, with hiring-company lead lists.
- [Nordic Company Registry API](https://apify.com/teddythebuilder/nordic-company-registry): company records from Sweden, Norway and Finland. Enrich hiring companies with org numbers and industry codes.
- [Nordic Tenders Scraper (TED API)](https://apify.com/teddythebuilder/nordic-public-tenders-monitor): public procurement tenders from Nordic buyers.
- [Tech Stack Detector](https://apify.com/teddythebuilder/tech-stack-detector): bulk-detect the CMS, e-commerce platform, analytics, CDN and email provider behind any list of company domains.

### Changelog and support

- **1.0 (2026-10):** first release. Greenhouse, Lever, Ashby, Workable, Recruitee and Teamtailor, with Teamtailor/Recruitee custom-domain detection, salary normalisation, only-new and removed-job monitoring, and description redaction.

Found a bug or a career site that doesn't work? Open an issue. We reply within 24 hours and usually ship a fix the same week.

# Actor input Schema

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

Company career pages to read, one per line. Examples: https://boards.greenhouse.io/gitlab, https://jobs.lever.co/spotify, https://jobs.ashbyhq.com/ramp, https://apply.workable.com/huggingface, https://bunq.recruitee.com, https://tibber.teamtailor.com, a Teamtailor custom domain like jobs.tibber.com, or shorthand like lever:spotify.

## `keywords` (type: `array`):

Optional. Keep jobs whose title, department or team contains ANY of these words (case-insensitive). Example: engineer, sales, data.

## `locations` (type: `array`):

Optional. Keep jobs whose location, country or workplace type contains ANY of these (case-insensitive). Example: Stockholm, Sweden, Germany, remote.

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

Keep only jobs marked as fully remote by the employer.

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

Optional. Keep only jobs first published in the last N days. Leave empty for all open jobs.

## `maxResults` (type: `integer`):

Maximum number of jobs to output (each is one billable result). Results are spread evenly across companies.

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

Add the job description as plain text (e-mail addresses and phone numbers removed).

## `maxDescriptionChars` (type: `integer`):

Truncate descriptionText to this many characters.

## `includeDescriptionHtml` (type: `boolean`):

Also add the original description HTML (mailto links and e-mail addresses removed).

## `onlyNew` (type: `boolean`):

Remember which jobs you already received (per monitor ID) and output only new ones. Schedule daily for new-job alerts. The first run outputs everything and stores the baseline.

## `emitRemoved` (type: `boolean`):

Output a row with status "removed" for jobs that were open in an earlier run but are gone now (filled or closed).

## `monitorId` (type: `string`):

Name of this saved search for monitoring. Use a different ID for each list of companies.

## Actor input object example

```json
{
  "companies": [
    "https://tibber.teamtailor.com",
    "https://jobs.lever.co/spotify",
    "https://boards.greenhouse.io/gitlab",
    "https://jobs.ashbyhq.com/ramp",
    "https://apply.workable.com/huggingface",
    "https://bunq.recruitee.com"
  ],
  "remoteOnly": false,
  "maxResults": 20,
  "includeDescription": true,
  "maxDescriptionChars": 6000,
  "includeDescriptionHtml": false,
  "onlyNew": false,
  "emitRemoved": false,
  "monitorId": "default"
}
```

# Actor output Schema

## `results` (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": [
        "https://tibber.teamtailor.com",
        "https://jobs.lever.co/spotify",
        "https://boards.greenhouse.io/gitlab",
        "https://jobs.ashbyhq.com/ramp",
        "https://apply.workable.com/huggingface",
        "https://bunq.recruitee.com"
    ],
    "maxResults": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("teddythebuilder/career-site-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": [
        "https://tibber.teamtailor.com",
        "https://jobs.lever.co/spotify",
        "https://boards.greenhouse.io/gitlab",
        "https://jobs.ashbyhq.com/ramp",
        "https://apply.workable.com/huggingface",
        "https://bunq.recruitee.com",
    ],
    "maxResults": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("teddythebuilder/career-site-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": [
    "https://tibber.teamtailor.com",
    "https://jobs.lever.co/spotify",
    "https://boards.greenhouse.io/gitlab",
    "https://jobs.ashbyhq.com/ramp",
    "https://apply.workable.com/huggingface",
    "https://bunq.recruitee.com"
  ],
  "maxResults": 20
}' |
apify call teddythebuilder/career-site-jobs-api --silent --output-dataset

```

## MCP server setup

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