# Job Postings API - 750k+ jobs, 7 ATS feeds, change-tracked (`roleset/job-postings-api`) Actor

One query, every ATS-hosted job, deduplicated, with what changed since yesterday. A normalized, change-tracked index built from official public feeds: Greenhouse, Lever, Ashby, SmartRecruiters, Recruitee, Personio and Workday.

- **URL**: https://apify.com/roleset/job-postings-api.md
- **Developed by:** [Roleset](https://apify.com/roleset) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 job records

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

## Job Postings API — 750k+ jobs from 7 ATS platforms, change-tracked

One query, every ATS-hosted job, deduplicated — and what changed since your last check.

This is an **index, not a scraper**. Job boards are read from the official public JSON
and XML feeds that Greenhouse, Lever, Ashby, SmartRecruiters, Recruitee, Personio and
Workday publish for exactly this purpose. Nothing is parsed out of a rendered page, no
`robots.txt` is worked around, and no login is required anywhere in the chain.

Boards are crawled on a schedule, stored, and diffed. When you run this actor you are
querying a database, not waiting on a live scrape — so a search over three quarters of a
million postings returns in seconds.

### Two modes

#### `search` — the market right now

Find open postings by keyword, title, company, domain, city, country, remote, ATS
platform, department, employment type or posting date.

```json
{ "mode": "search", "q": "platform engineer", "country": "US", "remote": true }
```

#### `changes` — only what moved

Every posting that **opened, changed, closed or reopened** since a timestamp, each with a
`diff` naming the fields that changed and their before and after values.

```json
{ "mode": "changes", "since": "2026-09-01T00:00:00Z" }
```

**Put this one on a schedule.** It returns each event once, so a daily run costs you the
handful of things that actually moved rather than the whole result set again. Re-running
`search` and diffing it yourself is both more expensive and less accurate — a posting
edited twice between runs looks like one change to a diff and is two events here.

### What a record contains

| | |
|---|---|
| **Identity** | stable id, title, normalized title, company, department, team |
| **Location** | structured `{city, region, country, remote}`, plus the raw string the employer wrote |
| **Terms** | employment type normalized across platforms; salary min/max/currency/interval where the board publishes one |
| **Links** | posting URL and apply URL |
| **Timing** | posted date, first seen, last seen, closed date |
| **Provenance** | which ATS and which board it came from, and when that board was last crawled |

Descriptions are included on request (`includeDescription`), in both HTML and plain text.

### No personal data

The index contains companies and roles, never people. There are no applicants, no
recruiter names and no contact details — person-shaped fields are discarded when a feed
is parsed, and email addresses and phone numbers are stripped out of descriptions before
anything is stored.

### Freshness

Every active board is crawled at least every 24 hours, and a board that changed on its
last crawl is revisited sooner. Each run reports the age of the data behind the exact
rows it returned, so you always know how fresh an answer is.

### Coverage

| Platform | Boards |
|---|---:|
| Greenhouse | 6,720 |
| SmartRecruiters | 5,811 |
| Personio | 4,724 |
| Ashby | 3,890 |
| Recruitee | 3,417 |
| Lever | 2,610 |
| Workday | 75 |

27,000+ boards, 26,000+ companies, 750k+ open postings.

**SmartRecruiters postings carry no description.** Their list endpoint does not include
one and fetching each posting separately is not reachable at a polite crawl rate. Every
other platform includes descriptions in full.

### Cost

Priced per record you actually receive — $1.00 per 1,000 — with nothing charged for a
run that returns nothing. Use `maxItems` to cap a run, or Apify's own maximum-charge
limit; the actor reads that limit before it starts and stops at it rather than fetching
data you will not be billed for.

Already a roleset.io customer? Put your key in `apiKey` and the run is billed to your
existing allowance instead — Apify will not also charge you per record.

### Links

- [roleset.io](https://roleset.io) — the API, the schema, and direct plans
- [API docs](https://api.roleset.io/docs)
- Questions: index@roleset.io

# Actor input Schema

## `mode` (type: `string`):

search: find open postings matching your filters. changes: only what opened, changed, closed or reopened — the one to schedule on a repeat run.

## `q` (type: `string`):

Free text over job title and description, e.g. "backend engineer".

## `title` (type: `string`):

Match on the normalized title. "Sr. Backend Engineer" also matches "Senior Backend Engineer".

## `company` (type: `string`):

Company name, partial match. Use `domain` instead for an exact company.

## `domain` (type: `string`):

Exact match, e.g. acme.com.

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

City or region, partial match — e.g. Berlin, California, Greater London.

## `country` (type: `string`):

ISO-3166 alpha-2, e.g. US, DE, GB.

## `remote` (type: `boolean`):

Leave unset for both remote and on-site roles.

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

Restrict to one applicant tracking system. Leave empty for all of them.

## `department` (type: `string`):

Department as the employer labels it — e.g. Engineering, Sales.

## `employment_type` (type: `string`):

Normalized across every platform: full\_time, part\_time, contract, intern, temporary or volunteer.

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

ISO-8601, e.g. 2026-09-01T00:00:00Z.

## `since` (type: `string`):

Changes mode only. ISO-8601. Defaults to the last 24 hours.

## `event` (type: `string`):

Changes mode only. Leave unset for all four.

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

Descriptions are large; leave off unless you need the body text.

## `include_closed` (type: `boolean`):

Include postings that have since closed. Off by default, so you get roles someone can still apply to.

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

Caps how many records you receive, and therefore what you are charged. Raise it once you know the shape of the data.

## `apiKey` (type: `string`):

Optional. Leave empty and this actor bills you per record through Apify. Supply your own roleset.io key and the run is billed to that account's allowance instead — Apify will not also charge you per record.

## Actor input object example

```json
{
  "mode": "search",
  "include_description": false,
  "include_closed": false,
  "maxItems": 100
}
```

# Actor output Schema

## `records` (type: `string`):

Every record this run returned, as JSON.

## `csv` (type: `string`):

The same records, flattened for 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("roleset/job-postings-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 = {}

# Run the Actor and wait for it to finish
run = client.actor("roleset/job-postings-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 '{}' |
apify call roleset/job-postings-api --silent --output-dataset

```

## MCP server setup

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