# Career Site Jobs API: Workday, Greenhouse, Salaries ($5/1K) (`plumblinedata/career-site-jobs-api`) Actor

$5 per 1,000 jobs, failed inputs free. Search 2M+ open jobs from company career sites: Workday, Greenhouse, Lever and 21 more job systems, refreshed nightly. Filter by title, place, salary.

- **URL**: https://apify.com/plumblinedata/career-site-jobs-api.md
- **Developed by:** [Plumbline](https://apify.com/plumblinedata) (community)
- **Categories:** Jobs, Lead generation, Agents
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 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

## Career Site Jobs API

Search over 2 million open jobs taken straight from company career sites, refreshed every night: Greenhouse, Lever, Ashby, Workday, SmartRecruiters, iCIMS, Workable, BambooHR, Personio, Oracle Cloud, UKG, ADP, Paylocity, Taleo and 10 more job systems, from more than 50,000 company job boards. Filter by job title, place, remote or hybrid, salary, seniority, company and posting date. Each row has the full description, salary range, skills, seniority, every location, and the company's hiring activity (new and closed jobs in the last 30 days). Closed jobs come with how long they stayed open.

- **Fast:** a search returns in seconds, because the jobs are already collected; nothing is scraped while you wait.
- **Fresh:** every job board is read again every night. Jobs that disappear are marked closed, not left in your results.
- **Real employers:** every job comes from the employer's own careers page, not a job aggregator. Staffing agencies are left out unless you turn them on.
- **Export:** JSON, CSV, Excel, Google Sheets, or the API. Works with Make, Zapier, n8n, and AI agents (MCP).

### What you can do with it

- **Run a job board or job alerts:** schedule a daily run with **Only new jobs since my last run** on. Each new job arrives once.
- **Find companies that are hiring:** search a role and a place, for example `data engineer` in `Germany`, to get the companies hiring for it. `companyNewJobs30d` shows how fast each one is growing. Hiring is a buying signal for sales teams and a lead for recruiters.
- **Salary and hiring research:** compare `salaryMin` and `salaryMax` by role, seniority and city. With **Job status** set to closed, `daysOpen` shows how long roles take to fill.
- **AI agents and job tools:** one call answers "remote senior Python jobs in Europe paying over 80k".

### How to use it

1. Type job title words (`data engineer`) and places (`Berlin`, `Texas`, `United States`, `EMEA`, `Remote`).
2. Add any other filters: remote or hybrid, posted within N days, minimum salary, seniority, companies.
3. Press **Start**. Rows appear in seconds.

Places are matched by meaning, not by exact text: `NYC` finds New York, `Bay Area` finds San Francisco, Oakland and San Jose, and a `Germany` search also includes remote jobs open to EMEA or worldwide (turn that off with **Include remote jobs open to these locations**).

### Input example

```json
{
  "keywords": ["data engineer", "analytics engineer"],
  "locations": ["Germany", "Netherlands"],
  "workArrangement": ["remote", "hybrid"],
  "postedWithinDays": 7,
  "maxResults": 100
}
```

### Good to know

- **Coverage:** over 2 million open jobs from more than 50,000 company job boards on 24 job systems, found by scanning the public web for career sites (about 112,000 boards are known; the others list no jobs, are retired, or are read for the first time over the next nights). Large employers on Workday fill in over the first nights, up to 1,500 new jobs per employer per night.
- **Company names:** `Walmart` finds Walmart Inc., and `Nvidia` finds NVIDIA USA and NVIDIA Germany: legal forms, countries and internal codes are ignored. A name never finds a different company that starts with it (`Amazon` is not Amazon Watch); write `Siemens*` for every employer whose name starts with Siemens, or a domain such as `stripe.com`.
- **A company is missing?** Run [Careers Page Jobs Scraper](https://apify.com/plumblinedata/careers-page-jobs-scraper) on its careers page to read it live. A company you ask for that the index does not hold gets an error row, which is free, listing the closest names the index does hold.
- **The same job in many cities** is one row with every location (`postingCount`, `mergedJobIds`). Turn off **One row per job posted in several places** to get one row per posting.
- **Empty results never fail silently:** a search with no match returns a free error row that says so.
- **Places:** every job gets a city, region and country where its location names them. A town given without a country ("Paderborn") gets one from [GeoNames](https://www.geonames.org) (CC BY 4.0) when only one country fits.

### Pricing

Pay per result: **$5 per 1,000 jobs**, plus **$0.001** per run start. No subscription.

| Event | When | Price |
| --- | --- | --- |
| Actor start | Once per run (per GB of memory; the default 1 GB counts as one) | $0.001 |
| Job | Each job row saved to the dataset | $0.005 |

| Example | Rows | Cost |
| --- | --- | --- |
| 10 jobs | 10 | $0.001 + $0.05 = **$0.051** |
| 100 jobs | 100 | $0.001 + $0.50 = **$0.501** |
| 1,000 jobs | 1,000 | $0.001 + $5.00 = **$5.001** |

- Error rows are free: a search with no match, or a company the index does not hold.
- On Apify's free plan a run stops at 20 rows. Any paid Apify plan removes this limit.
- Set **Max total charge per run** in the run options. The Actor stops cleanly at that limit and keeps every row saved so far.

### Use it from code, automations, and AI agents

**API** (returns the rows when the run finishes):

```bash
curl -X POST "https://api.apify.com/v2/acts/plumblinedata~career-site-jobs-api/run-sync-get-dataset-items?token=YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"keywords": ["data engineer"], "locations": ["Germany"], "postedWithinDays": 7, "maxResults": 100}'
```

**Python** (`pip install apify-client`):

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_API_TOKEN")
run = client.actor("plumblinedata/career-site-jobs-api").call(run_input={"keywords": ["data engineer"], "locations": ["Germany"], "postedWithinDays": 7})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(row["title"], row["company"], row["location"])
```

**No-code:** connect the Actor in Make, Zapier, n8n, or Google Sheets through Apify's integrations, or add a webhook to get notified when a run finishes.

**AI agents (MCP):** add `https://mcp.apify.com?tools=plumblinedata/career-site-jobs-api` to Claude, Cursor, or any MCP client. The agent can search jobs with the same input as above.

### FAQ

**How fresh are the jobs?** Every board is read once a night. `lastSeenAt` is the last time the job was on its board; `firstSeenAt` is when it first appeared.

**Can I get only new jobs every day?** Yes. Turn on **Only new jobs since my last run** and schedule the Actor. The first run returns current matches; later runs return only jobs added since.

**Is the data legal to use?** Every job comes from a public careers page that the employer publishes so people can apply. No logins, no personal data beyond what the employer posts. You are responsible for how you use the data, including copyright, personal data rules such as GDPR, and the site's terms.

**What does an error row mean?** A search with no match returns one row with `type: "error"`, a short `errorCode` (`no_matching_jobs`, `no_new_jobs`, `company_not_indexed`, `company_ambiguous`, `index_unavailable`) and a plain-language `errorMessage`. Error rows are free. Remove them with a filter on `type`.

**How is this different from Careers Page Jobs Scraper?** This Actor searches jobs we have already collected, so a search across thousands of companies returns in seconds. [Careers Page Jobs Scraper](https://apify.com/plumblinedata/careers-page-jobs-scraper) reads the careers pages you give it, live, including companies this index does not hold.

### Support

Found a problem or need a field? Open an issue on the **Issues** tab. Include the run ID and the input that shows the problem. We read every issue.

# Changelog

This Actor's version history is a separate document: https://apify.com/plumblinedata/career-site-jobs-api/changelog.md

# Actor input Schema

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

Keep jobs whose title contains any of these, as whole words: `data engineer` matches 'Senior Data Engineer' but `java` never matches 'JavaScript'. Leave empty for any title.

## `excludeKeywords` (type: `array`):

Skip jobs whose title contains any of these words, for example `intern`, `manager`.

## `matchKeywordsIn` (type: `string`):

Title only is precise. Adding skills and requirements also finds jobs that ask for the word (for example `dbt`) without naming it in the title.

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

Cities, states, countries or regions: `Berlin`, `Texas`, `Germany`, `DE`, `NYC`, `Bay Area`, `Europe`, `Remote`. A job matches if any of its locations does. Remote jobs open to a searched country (by name, by a region like EMEA, or worldwide) are included; turn that off below.

## `includeRemoteForLocation` (type: `boolean`):

A remote job listed for 'EMEA' or 'Worldwide' matches a Germany search. Turn off to keep only jobs located in the place.

## `workArrangement` (type: `array`):

Keep only these. Leave empty for all.

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

Keep jobs posted in the last N days. Jobs whose board gives no post date count from the day the index first saw them.

## `onlyNewSinceLastRun` (type: `boolean`):

For scheduled runs: return only jobs added to the index since this same search last ran, so each new job arrives once. The first run returns the current matches.

## `salaryMin` (type: `integer`):

Keep jobs whose posted pay reaches this per year. Hourly, daily, weekly and monthly pay is converted to yearly. Jobs with no posted pay are left out when this is set.

## `salaryCurrency` (type: `string`):

ISO code for the minimum salary, for example `USD`, `EUR`, `GBP`. Leave empty to compare the number in any currency.

## `onlyWithSalary` (type: `boolean`):

Keep only jobs that state a salary range.

## `experienceLevels` (type: `array`):

Keep only these levels (read from the title, the years of experience asked for, and the board's own level).

## `employmentTypes` (type: `array`):

Keep only these types.

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

Company names or website domains, for example `Walmart`, `stripe.com`. A name finds the company under its legal and country names (`Walmart` finds Walmart Inc., `Nvidia` finds NVIDIA USA and NVIDIA Germany), never a different company that starts with it. End a name with `*` for every employer that starts with it (`Siemens*`). A company the index does not hold gets an error row, which is not charged.

## `excludeCompanies` (type: `array`):

Names or domains to skip, matched the same way (`*` at the end for every name that starts with it).

## `visaSponsorship` (type: `boolean`):

Keep jobs whose description says the employer sponsors visas.

## `includeStaffingAgencies` (type: `boolean`):

Jobs posted by recruiting and staffing agencies are left out unless this is on.

## `mergeLocations` (type: `boolean`):

Many companies post the same job once per city. On: one row listing every matching city (`postingCount`, `mergedJobIds`). Off: one row per posting.

## `jobStatus` (type: `string`):

Closed jobs carry `closedAt` and `daysOpen`: how long each role stayed open.

## `jobBoards` (type: `array`):

Leave empty for all.

## `languages` (type: `array`):

Keep jobs written in these languages, for example `English`, `German`.

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

Add `descriptionText` and `descriptionHtml`. Turn off for faster, smaller exports; salary, skills and the other fields are still filled.

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

Stop after this many jobs, newest first.

## Actor input object example

```json
{
  "keywords": [
    "data engineer",
    "analytics engineer"
  ],
  "matchKeywordsIn": "title",
  "locations": [
    "United States",
    "Germany"
  ],
  "includeRemoteForLocation": true,
  "postedWithinDays": 7,
  "onlyNewSinceLastRun": false,
  "onlyWithSalary": false,
  "visaSponsorship": false,
  "includeStaffingAgencies": false,
  "mergeLocations": true,
  "jobStatus": "open",
  "includeDescription": true,
  "maxResults": 100
}
```

# Actor output Schema

## `results` (type: `string`):

Dataset of matching jobs from company career sites.

# 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 = {
    "keywords": [
        "data engineer",
        "analytics engineer"
    ],
    "locations": [
        "United States",
        "Germany"
    ],
    "postedWithinDays": 7,
    "maxResults": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("plumblinedata/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 = {
    "keywords": [
        "data engineer",
        "analytics engineer",
    ],
    "locations": [
        "United States",
        "Germany",
    ],
    "postedWithinDays": 7,
    "maxResults": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("plumblinedata/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 '{
  "keywords": [
    "data engineer",
    "analytics engineer"
  ],
  "locations": [
    "United States",
    "Germany"
  ],
  "postedWithinDays": 7,
  "maxResults": 100
}' |
apify call plumblinedata/career-site-jobs-api --silent --output-dataset

```

## MCP server setup

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