# Career Site Job Listing API (`jobscrawler/career-site-job-listing-api`) Actor

Extract live career-site jobs from public Greenhouse, Ashby, and Lever board APIs. Prefilled board URLs, full descriptions, filters, and source provenance. No API key required in direct mode; optional Fantastic.jobs keyed mode.

- **URL**: https://apify.com/jobscrawler/career-site-job-listing-api.md
- **Developed by:** [Jobs Scraper](https://apify.com/jobscrawler) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 65.4% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.99 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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 Job Listing API

### Overview

This folder contains a complete, standalone Apify Actor for rich career-site and applicant-tracking-system job data. Its project layout follows the workspace’s `jobright-scraper`: executable code is under `src/`, Apify definitions are under `.actor/`, the container is defined by `Dockerfile`, and `dataset-validator.js` checks every locally generated record. The Actor does not import code from any sibling scraper, so it can be pushed, built, scheduled, and versioned on its own.

Live mode calls the official Fantastic.jobs `/v1/active-ats` endpoint. It requests descriptions, public company enrichment, occupation classifications, and structured geocoded locations, then normalizes the response into a stable schema. Live runs require `fantasticJobsApiKey` or the `FANTASTIC_JOBS_API_KEY` environment variable. The secret is used only in the Authorization header; it is never printed, stored in the dataset, or written to diagnostics.

### Rich output

The normalized record includes stable IDs, title, company, all locations, salary range and period, employment type, workplace mode, dates, full description text and HTML, responsibilities, requirements, benefits, skills, keywords, taxonomies, education, language, visa sponsorship, application URLs, recruiter details, hiring-manager fields, LinkedIn organization enrichment, company size and funding, occupational classifications, and structured locations. Every upstream field remains available under `rawData`.

No source value is invented. Null and undefined values are removed recursively from `rawData`. Normalized optional fields use empty strings, arrays, or objects, so the dataset contains no JSON `null` values while still distinguishing “not published” from factual data. `extractionStatus`, `dataMode`, and `sampleProvenance` make the origin and completeness of each record explicit.

### Inputs

The complete machine-readable contract is in `.actor/input_schema.json`. Useful inputs are `keyword`, `location`, `organization`, `timeRange`, `descriptionFormat`, `includeCompanyDetails`, `includeBetaFields`, and `advancedFilters`. `maxItems` defaults to ten and the runtime enforces a minimum request of ten. Advanced filters accept documented primitive Fantastic.jobs query parameters; input is never evaluated as code.

### Local development

Install dependencies and run an explicit official-sample contract test:

```bash
npm install
apify run --purge --input '{"sampleMode":true,"maxItems":10}'
npm run validate:data
```

Then run live with a secret key:

```bash
apify run --purge --input '{"fantasticJobsApiKey":"<secret>","keyword":"software engineer","timeRange":"7d","maxItems":10}'
npm run validate:data
```

Sample mode reads Fantastic.jobs’ published ATS sample and labels every record `official_sample`. It verifies normalization, schema, storage, and validation only; it must not be presented as a live search result. Live mode never falls back silently to sample data.

### Run status and production

The default dataset contains records. `RUN_SUMMARY` reports status, requested and returned counts, provenance mode, timing, and null-validation count. `SOURCE_METADATA` records the provider and endpoint. A failure creates a sanitized `ERROR_DIAGNOSTIC` and never creates fake jobs or misclassifies an access or credential error as `no_results`.

Deploy this folder independently with `apify push`. Configure secrets in Apify’s encrypted input or environment settings, not in source files. Respect upstream API plan limits, terms, privacy requirements, and retention rules when scheduling or redistributing the public job data.

### Source identity

- Standalone Actor: `career-site-job-listing-api`
- Original catalog reference ID: `s3dtSTZSZWFtAVLn5`
- Provider: Fantastic.jobs active ATS API
- Record type: rich job
- Default output request: `10`

## Live source update (2026-10-21)

The default `dataSource: "direct"` reads current public Greenhouse, Ashby, and Lever board APIs from `startUrls`. No vendor API key is needed. Default URLs are prefilled in the input form. Full source descriptions and provenance are returned; no sample or synthetic fallback is allowed. Empty valid searches return zero records; unavailable sources fail instead of pretending success.

Use `dataSource: "fantastic"` with your `fantasticJobsApiKey` for the original paid feed, vendor time windows, and enrichment. Direct mode does not provide Fantastic.jobs enrichment or historical time-window filtering. The older vendor-specific reference below applies only to Fantastic mode.

# Changelog

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

# Actor input Schema

## `dataSource` (type: `string`):

Direct reads current public board APIs without a vendor API key. Fantastic requires your paid API key and enables vendor filters/enrichment.

## `startUrls` (type: `array`):

Greenhouse, Ashby, or Lever board URLs, restricted to the Actor platform. Custom hosts are rejected.

## `fantasticJobsApiKey` (type: `string`):

Bearer API key used only for live Fantastic.jobs requests. It is never logged.

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

Optional full-text title filter. Leave empty to return the newest available jobs.

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

Optional full-name location filter, for example Germany or Berlin, Germany.

## `organization` (type: `string`):

Case-insensitive company name filter for direct mode; exact vendor filter in Fantastic mode.

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

Maximum live records to return.

## `timeRange` (type: `string`):

Fantastic mode only: active-feed time window. Direct mode returns currently published jobs, not a historical archive.

## `descriptionFormat` (type: `string`):

Request plain text or source HTML descriptions. HTML preserves the richest payload.

## `includeCompanyDetails` (type: `boolean`):

Fantastic mode only: request vendor company enrichment. Direct mode returns fields available publicly.

## `includeBetaFields` (type: `boolean`):

Fantastic mode only: request vendor beta classifications. Direct mode does not invent enrichment.

## `advancedFilters` (type: `object`):

Optional Fantastic.jobs query parameters such as has_salary, ai_work_arrangement, domain, or organization_industry.

## `requestTimeoutSecs` (type: `integer`):

Per-request timeout in seconds.

## Actor input object example

```json
{
  "dataSource": "direct",
  "startUrls": [
    {
      "url": "https://job-boards.greenhouse.io/duolingo"
    },
    {
      "url": "https://jobs.ashbyhq.com/openai"
    }
  ],
  "keyword": "",
  "location": "",
  "organization": "",
  "maxItems": 10,
  "timeRange": "7d",
  "descriptionFormat": "html",
  "includeCompanyDetails": true,
  "includeBetaFields": true,
  "advancedFilters": {},
  "requestTimeoutSecs": 45
}
```

# Actor output Schema

## `dataset` (type: `string`):

Dataset containing normalized records and complete cleaned rawData.

## `runSummary` (type: `string`):

Machine-readable completion status, provenance mode, counts, and validation information.

## `files` (type: `string`):

Key-value store containing source metadata and any error diagnostic.

# 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 = {
    "startUrls": [
        {
            "url": "https://job-boards.greenhouse.io/duolingo"
        },
        {
            "url": "https://jobs.ashbyhq.com/openai"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("jobscrawler/career-site-job-listing-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 = { "startUrls": [
        { "url": "https://job-boards.greenhouse.io/duolingo" },
        { "url": "https://jobs.ashbyhq.com/openai" },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("jobscrawler/career-site-job-listing-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 '{
  "startUrls": [
    {
      "url": "https://job-boards.greenhouse.io/duolingo"
    },
    {
      "url": "https://jobs.ashbyhq.com/openai"
    }
  ]
}' |
apify call jobscrawler/career-site-job-listing-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,jobscrawler/career-site-job-listing-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/ftlWj1CkpYMBdOgto/builds/EpM2jA40ATcFGsayR/openapi.json
