# Career Site Job Scraper: Greenhouse, Lever, Ashby, Workday (`fairfetch/career-site-jobs-api`) Actor

Get every open job from company career sites on Greenhouse, Lever, Ashby, Workday and SmartRecruiters via their public job-board feeds. Normalized fields, salary ranges, clean descriptions, filters, and new/closed job tracking between runs. $1.50 per 1,000 jobs.

- **URL**: https://apify.com/fairfetch/career-site-jobs-api.md
- **Developed by:** [Naveen Raja](https://apify.com/fairfetch) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.50 / 1,000 job results

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?

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 Scraper & API: Greenhouse, Lever, Ashby, Workday + new/closed job tracking

Get **every open job from company career sites** that run on **Greenhouse, Lever, Ashby, Workday and SmartRecruiters**, in one clean JSON format. The Actor reads the public job-board feeds that the companies' own careers pages use, so the data is complete, current and fast to fetch. No browser, no login, no proxy.

Switch on **change tracking** and every later run tells you which jobs are **new** and which were **closed** (taken down) since the previous run. Schedule it daily and you have a hiring monitor.

**Price: $1.50 per 1,000 jobs** ($0.0015 per job) plus $0.0005 per run. You pay only for job rows returned; Apify platform costs are included.

| What you run | Cost |
|---|---|
| 100 jobs | $0.1505 |
| Every open job at a 700-role company (e.g. Stripe) | about $1.10 |
| Daily "only new and closed" monitor of 20 companies (~50 changes a day) | about $0.08 a day |

### What you get

- One normalized item per job, same fields for every ATS: company, title, department, team, locations, remote status, workplace type, employment type, posted date, job URL, apply URL, **salary range** (when the company publishes one) and an optional **clean-text job description**.
- **New / existing / closed status** for each job when tracking is on, with the time we first saw the job.
- Filters: keywords, excluded words, locations, departments, remote only, posted in the last N days.
- Paste careers-page URLs and the ATS is detected for you, or use short `ats:slug` entries.
- Built for automation and AI agents: predictable schema, pay-per-event pricing with no usage fees, works with limited permissions, a run summary record, and a clean stop when your spending limit is reached.

### Supported job boards

| ATS | Example input | What the ATS publishes |
|---|---|---|
| Greenhouse | `https://job-boards.greenhouse.io/stripe` or `greenhouse:stripe` | Title, department, location, first published / updated dates, pay ranges (pay transparency), description |
| Lever | `https://jobs.lever.co/palantir` or `lever:palantir` (EU boards on `jobs.eu.lever.co` too) | Title, team, department, all locations, workplace type, commitment, created date, salary range, description |
| Ashby | `https://jobs.ashbyhq.com/openai` or `ashby:openai` | Title, department, team, primary + secondary locations, remote flag, workplace type, employment type, published date, compensation, description |
| Workday | `https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite` or `workday:nvidia.wd5/NVIDIAExternalCareerSite` | Title, all locations, time type, start/posted date, remote type (some sites), description |
| SmartRecruiters | `https://jobs.smartrecruiters.com/ServiceNow` or `smartrecruiters:ServiceNow` | Title, department, function, location with remote/hybrid flags, employment type, release date, description |

You can also paste a company's own careers page (for example `https://www.figma.com/careers/`). The Actor opens that page once and looks for an embedded Greenhouse, Lever, Ashby, Workday or SmartRecruiters board. This works for many sites but not all (some pages are built entirely in JavaScript or block automated visits); if it fails, the run summary tells you to pass the ATS URL instead.

### Use cases

- **Job boards and aggregators**: pull complete, fresh listings straight from employers' career sites.
- **Hiring signals for sales and investing**: see which companies are hiring for which teams and where, and get alerted when new roles open or close.
- **Recruiting and talent intelligence**: track competitors' open roles, locations and published salary ranges.
- **Job alerts**: schedule a daily run with keywords and "Return only new and closed jobs" to get just the changes.
- **AI agents and LLM apps**: give an agent a reliable tool to answer "Is Stripe hiring data engineers in Dublin?" with structured data.

### How to use

1. Add companies in **Companies** (one per line). Careers-page URLs or `ats:slug` entries both work.
2. Optional: add **filters** (keywords, locations, departments, remote only, posted within N days).
3. Choose whether to **include the job description** (on by default).
4. Optional: turn on **Track new and closed jobs**. Use the same **Tracking ID** on every run of the same monitor.
5. Run the Actor and download the results as JSON, CSV, Excel or via API. A per-company summary is saved as the `SUMMARY` record in the run's key-value store.

The same board entered twice (for example `greenhouse:stripe` and `https://job-boards.greenhouse.io/stripe`, or a careers page that leads to a board you also listed) is read and charged once. An entry that returns no jobs because of a problem (a wrong slug, no board found on a careers page, an error at the job board) gives **one free error row** in the dataset with `status: "error"`, the `input`, an `errorKind` and the reason in `error`.

#### Input example

```json
{
  "companies": [
    "greenhouse:stripe",
    "https://jobs.lever.co/palantir",
    "ashby:openai",
    "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite",
    "smartrecruiters:ServiceNow"
  ],
  "keywords": ["engineer", "data"],
  "locations": ["San Francisco", "Remote"],
  "includeDescription": true,
  "maxJobsPerCompany": 0,
  "trackChanges": true,
  "onlyNewAndClosed": false,
  "trackingId": "my-hiring-monitor"
}
```

#### Output example

A real item from a test run in October 2026 (Ramp on Ashby, with **Remote jobs only**; description shortened):

```json
{
  "company": "ramp",
  "companySlug": "ramp",
  "ats": "ashby",
  "jobId": "d1183b00-6590-4fe4-a585-28d84e578fe3",
  "uid": "ashby:ramp:d1183b00-6590-4fe4-a585-28d84e578fe3",
  "title": "Software Engineer, Security, Stablecoin",
  "department": "Engineering",
  "team": "Backend",
  "locations": [
    "New York, NY (HQ)",
    "San Francisco, CA",
    "Remote (US)"
  ],
  "remote": true,
  "workplaceType": "remote",
  "employmentType": "Full-time",
  "postedAt": "2026-05-13T20:28:09Z",
  "updatedAt": null,
  "url": "https://jobs.ashbyhq.com/ramp/d1183b00-6590-4fe4-a585-28d84e578fe3",
  "applyUrl": "https://jobs.ashbyhq.com/ramp/d1183b00-6590-4fe4-a585-28d84e578fe3/application",
  "compensation": {
    "min": 189000,
    "max": 330000,
    "currency": "USD",
    "interval": "year",
    "summary": "$189K – $330K • Offers Equity"
  },
  "status": "new",
  "firstSeenAt": "2026-10-19T22:37:26Z",
  "scrapedAt": "2026-10-19T22:37:26Z",
  "input": "ashby:ramp",
  "error": null,
  "errorKind": null,
  "descriptionText": "About Ramp\n\nRamp is building the smart infrastructure for finance teams, embedded in the transaction flow of every dollar a business spends. We automa ..."
}
```

A **closed** job (tracking on) has the same shape with `"status": "closed"`. It keeps the title, locations, department, team, URLs, remote status, workplace type and `firstSeenAt` saved when the job was first returned; the other fields are `null` because the posting no longer exists. (For jobs saved by builds before 1.1.2, `remote` is recomputed from the saved locations, often `null`, and `workplaceType` is `null`; their closures are always reported, also with **Remote jobs only** or the location "Remote", because the monitor returned those jobs before.)

An **error row** (free) has `"status": "error"`, the `input` it belongs to, `errorKind` (`not_found`, `invalid_input`, `no_board_found`, `careers_page_unreadable`, `source_error` or `internal_error`) and `error` (what went wrong and what to do); the job fields are `null`.

#### Output fields

| Field | Description |
|---|---|
| `company`, `companySlug`, `ats` | Company name as published by Greenhouse and SmartRecruiters. Lever, Ashby and Workday don't publish a company name, so for them it is the board name (the slug, e.g. `openai`, or the Workday tenant, e.g. `nvidia`). Then the board identifier and the ATS |
| `jobId`, `uid` | Job ID on the ATS, and a globally unique `ats:companySlug:jobId` key |
| `title`, `department`, `team` | As published by the company |
| `locations` | List of location strings |
| `remote` | `true`: remote (see "Remote jobs" below). `false`: the ATS says hybrid or on-site. `null`: the ATS doesn't say |
| `workplaceType` | `remote` / `hybrid` / `onsite` as published by the ATS, otherwise `null` |
| `employmentType` | Full-time, Part-time, Contract, Internship, ... |
| `postedAt`, `updatedAt` | ISO 8601 (UTC). Workday gives a date only. Lever and Ashby have no update date |
| `url`, `applyUrl` | Job page and application page |
| `compensation` | `{min, max, currency, interval, summary}` or `null` |
| `descriptionText` | Clean plain-text description (when **Include job description** is on) |
| `status` | `open` (tracking off), `new` / `existing` / `closed` (tracking on), or `error` (free error row) |
| `firstSeenAt`, `scrapedAt` | When this tracker first returned the job, and when this run read it |
| `input` | The company entry from your input that produced the row |
| `error`, `errorKind` | Error rows only (`null` on job rows): why the entry returned no jobs |

The dataset has ready-made views: **Jobs**, **New & closed**, **Compensation**, **Errors** and **All fields**. Apify views can't hide rows, so error rows also appear in the job views as nearly empty rows with status `error`. To keep only jobs, skip rows whose `status` is `error` (in a spreadsheet, filter the Status column; in code, `[r for r in items if r["status"] != "error"]`). Failed entries are also listed in the `SUMMARY` record.

#### Remote jobs

The `remote` field and the **Remote jobs only** filter follow the same rules for every ATS:

1. A workplace type published by the ATS wins. Hybrid and on-site jobs are `remote: false`, even when the ATS also flags them as remote-eligible (Ashby sets `isRemote: true` on hybrid postings) or one of their locations says "Remote".
2. Without a workplace type, an explicit remote flag from the ATS decides (Ashby `isRemote`).
3. Otherwise a location that clearly says remote ("Remote", "US-Remote", "Remote - Canada", "Remote-Friendly, United States") gives `remote: true` (with `workplaceType: null`, because a location such as "US-SF, US-NYC, US-Remote" means remote is possible, not that the job is fully remote).
4. Otherwise the remote status is unknown: `remote: null`. We don't guess from the description text.

What each ATS publishes: Lever, SmartRecruiters and most Ashby boards give a workplace type for every job. Greenhouse gives one only when the company adds it as a custom field (many don't), and Workday only on sites that publish a "remote type". On those boards, jobs without a "Remote" location are `null`. **Remote jobs only** keeps `remote: true` jobs; tick **Remote only: also keep jobs with unknown remote status** to also get the `null` ones and check them yourself. The location filter value "Remote" works like **Remote jobs only**, including that option.

### How change tracking works

With **Track new and closed jobs** on, the Actor saves the jobs it has returned for each company in a named key-value store in your Apify account (`career-site-jobs-tracking`, one record per company and Tracking ID). On the next run:

- **new**: on the board now and not returned before by this tracker
- **existing**: returned in an earlier run and still on the board
- **closed**: returned in an earlier run and no longer on the board (it is output once, then forgotten)

Good to know:

- The first run marks every job as `new`.
- **Return only new and closed jobs** skips `existing` jobs, so a daily monitor costs you only for the changes. Ticking it turns change tracking on by itself.
- Jobs that don't match your filters are not stored, so if you widen the filters later, newly matching jobs show up as `new`.
- Closed jobs are reported only when the whole board could be read in that run (for example not when a very large Workday site could not be read completely). If a run stops early because of your spending limit, unreported changes are kept and reported next time.
- Use different **Tracking IDs** for independent monitors (for example one per team or per filter set).

### Pricing

Pay per event, no subscription and no extra usage fees:

| Event | Price |
|---|---|
| `job`: one job row in the dataset (open, new, existing or closed) | **$0.0015** ($1.50 per 1,000) |
| Actor start (standard Apify event, per run; one event per GB of memory, so 1 at the default 512 MB) | $0.0005 |

Examples: all 731 open jobs at Stripe cost about $1.10. A daily "only new and closed" monitor of 20 companies that sees 50 changes a day costs about $0.08 per day.

- Apify platform costs (compute, storage, data transfer) are included in the price.
- Free: error rows, the run summary (`SUMMARY` record), duplicate board entries (read once), and jobs whose detail page could not be loaded (skipped, not charged).
- Set **Maximum cost per run** in the run options (or `maxTotalChargeUsd` via API) to cap spending. The Actor stops cleanly when the cap is reached: it stops listing boards and loading job details as soon as it can't charge for more rows, and everything returned up to that point is in the dataset.
- If Apify moves a run to another server mid-run, it continues where it stopped and never returns or charges a job twice.

### Changes in version 1.1 (20 Oct 2026)

What existing users may notice:

- **Error rows in the dataset.** An entry that returns no jobs because of a problem now gives one free row with `status: "error"`, `input`, `error` and `errorKind`; all job fields are `null`. To keep only jobs, skip rows whose `status` is `error`. Every row now has the keys `input`, `error` and `errorKind` (so CSV/Excel exports have three more columns; `error` and `errorKind` are `null` on job rows).
- **Remote status.** `remote` is `null` (unknown) instead of `false` when the job board doesn't say, and hybrid or on-site jobs are always `false` (Ashby hybrids used to be `true`). **Remote jobs only** and the location value "Remote" now return only remote jobs; tick **Remote only: also keep jobs with unknown remote status** to include the unknown ones.
- **`workplaceType`** is only what the job board publishes: Greenhouse jobs that are remote only according to their location text now have `workplaceType: null` (was `remote`).
- **Board ids.** Greenhouse and Ashby slugs are lower-case in `companySlug` and `uid` (`greenhouse:Figma` gives `greenhouse:figma:…`), and SmartRecruiters uses its own spelling of the company id (`ServiceNow`). The same board entered twice is read and charged once. Change tracking keeps working: records saved by version 1.0 under the old spelling are taken over on the first 1.1 run, so jobs stay `existing`.
- **Tracking.** **Return only new and closed jobs** now turns change tracking on by itself (it creates the `career-site-jobs-tracking` store).
- **Run status.** A run where every entry fails because of the input (for example a wrong slug) now ends as succeeded, with the error rows explaining why.

### Use it from the API, an app or an AI agent

Run synchronously and get the items in one call (keep your own API token in the `APIFY_TOKEN` environment variable):

```bash
curl -X POST "https://api.apify.com/v2/acts/fairfetch~career-site-jobs-api/run-sync-get-dataset-items?maxTotalChargeUsd=0.50" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"companies": ["greenhouse:stripe"], "keywords": ["data"], "includeDescription": false}'
```

The Actor runs with limited permissions and pay-per-event pricing without usage fees, so AI agents can call it through the Apify MCP server and pay per run. Each run also writes a machine-readable `SUMMARY` record with per-company counts and errors.

### Performance and limits

- Greenhouse, Lever, Ashby and SmartRecruiters boards are read with one request per board or per 100 jobs. In our tests a 700 to 800-job board took 10 to 50 seconds, and 5,288 jobs from five companies took 2.5 minutes (without descriptions).
- **Max jobs per company** returns the newest jobs on every ATS. Greenhouse and Ashby send the whole board in one response, and Lever boards are always read in full because Lever doesn't list jobs by date. Workday and SmartRecruiters list newest first, so without filters or tracking only the first pages are read.
- Workday and SmartRecruiters need one extra request per job for the description, so large boards with descriptions take longer (roughly 1 to 2 jobs per second per company). Turn off **Include job description** for speed.
- Workday search returns at most 2,000 jobs per query. For bigger sites the Actor automatically splits the query by job category, so you still get the full board (tested: 2,735 NVIDIA jobs). If a split can't cover the whole site, the run says so in `SUMMARY` and skips closed-job detection for it, so no job is wrongly reported as closed.
- Requests are polite: at most 3 at a time per host, automatic retries with exponential backoff for errors and rate limits, and timeouts.
- Fields depend on what each company publishes. For example, many Greenhouse boards don't publish employment type, and Workday sites don't publish departments (the department filter is skipped for Workday).

### FAQ

**Do I need an account with Greenhouse, Lever or the other ATSs?**
No. The Actor uses the public job-board feeds that the career sites themselves use.

**Do I need proxies?**
No. The job-board APIs are public and the Actor keeps request rates low. A proxy option exists for edge cases.

**How do I find a company's ATS slug?**
Open the company's careers page and click a job. The URL usually shows it: `job-boards.greenhouse.io/<slug>`, `jobs.lever.co/<slug>`, `jobs.ashbyhq.com/<slug>`, `<tenant>.wd5.myworkdayjobs.com/<site>` or `jobs.smartrecruiters.com/<slug>`. You can paste that URL directly.

**Why is a company missing jobs I can see on its website?**
Some companies show jobs from several boards or use an ATS that isn't supported yet. Check which board the job links point to and add that board.

**Can I get only jobs posted recently?**
Yes, set **Posted within the last N days**. For an alerting workflow, change tracking with **Return only new and closed jobs** is more reliable because it does not depend on posting dates.

**What happens if one company fails?**
The other companies still run. The failed entry gets a free error row in the dataset, and the reason is also in the `SUMMARY` record and the run status message. A wrong slug returns "Job board not found (HTTP 404)". SmartRecruiters answers "0 jobs" for company IDs that don't exist, so an ID without jobs also gets an error row asking you to check it. If every entry fails because of the input, the run still ends as succeeded, with the error rows explaining why.

**Why is `remote` null for some jobs?**
The ATS doesn't say whether those jobs are remote (common on Greenhouse boards without a workplace field and on Workday sites without a "remote type"). See "Remote jobs" above.

**Is there a limit on companies per run?**
No hard limit. Companies are processed a few at a time; very large lists simply take longer.

### Legal

Not affiliated with or endorsed by Greenhouse, Lever, Ashby, Workday, SmartRecruiters or any employer whose jobs it reads; these names are used only to describe compatibility.

This Actor only reads job postings that companies publish openly on their public career sites, through the same public endpoints those sites use. It does not log in, bypass access controls or collect personal data about candidates. Job postings may include the name of a hiring contact in rare cases; handle any personal data you receive in line with the laws that apply to you (such as GDPR). You are responsible for how you use the data and for complying with the terms of the websites you read.

# Actor input Schema

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

One company job board per line. Accepted forms:

- Greenhouse: https://job-boards.greenhouse.io/stripe or greenhouse:stripe
- Lever: https://jobs.lever.co/palantir or lever:palantir (EU boards on jobs.eu.lever.co work too)
- Ashby: https://jobs.ashbyhq.com/openai or ashby:openai
- Workday: https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite or workday:nvidia.wd5/NVIDIAExternalCareerSite
- SmartRecruiters: https://jobs.smartrecruiters.com/ServiceNow or smartrecruiters:ServiceNow
- A company's own careers page (e.g. https://example.com/careers): the page is scanned for an embedded Greenhouse, Lever, Ashby, Workday or SmartRecruiters board.
  The same board entered twice (for example as a URL and as "ats:slug") is read and charged once. An entry that returns no jobs because of a problem (wrong slug, no board found, site error) gives one free error row with the reason.

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

Keep only jobs whose title, department or team contains at least one of these words (case-insensitive). Leave empty to keep all jobs.

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

Drop jobs whose title contains any of these words, e.g. "intern" or "senior".

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

Keep only jobs with a location containing one of these texts (case-insensitive), e.g. "London", "United States". The special value "Remote" uses the same rule as "Remote jobs only" (including the option to keep jobs with unknown remote status), so hybrid or on-site jobs that list a "Remote" location are not matched.

## `departments` (type: `array`):

Keep only jobs whose department or team contains one of these texts. Workday sites do not publish departments, so this filter is skipped for Workday.

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

Keep only remote jobs: the ATS marks the job as remote, or (when the ATS gives no workplace type) a location says "Remote", e.g. "US-Remote". Hybrid and on-site jobs are never included, even when the ATS also flags them as remote-eligible. Jobs whose ATS doesn't say whether they are remote (output `remote: null`, common on Greenhouse and Workday) are left out unless you tick the next option.

## `includeUnknownRemote` (type: `boolean`):

With "Remote jobs only": also keep jobs whose ATS doesn't say whether they are remote (`remote: null`), so you can check them yourself. Hybrid and on-site jobs stay excluded.

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

Keep only jobs first published in the last N days (where the ATS publishes a date). Leave empty for no limit.

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

Add the full job description converted from HTML to clean plain text. For Workday and SmartRecruiters this needs one extra request per job, so runs take longer.

## `maxJobsPerCompany` (type: `integer`):

Maximum number of matching jobs returned per company, newest first (by the date the ATS publishes; undated jobs last). 0 = no limit. Lever boards are always read in full to find the newest, because Lever does not list them by date; Workday and SmartRecruiters list newest first, so only the first pages are read when no filters or tracking are used. Closed jobs reported by change tracking are not counted against this limit.

## `trackChanges` (type: `boolean`):

Remember the jobs returned for each company in a named key-value store, so every later run marks jobs as "new" or "existing" and also returns jobs that have been taken down since the previous run as "closed". Schedule the Actor (e.g. daily) to monitor hiring. Turned on automatically when "Return only new and closed jobs" is ticked.

## `onlyNewAndClosed` (type: `boolean`):

Skip jobs that were already returned in an earlier run, so you only pay for changes. Needs change tracking, which this option turns on by itself. The first run returns every job as "new".

## `trackingId` (type: `string`):

Name of the tracking state (used only with change tracking). Runs with the same ID share what has been seen; use different IDs for independent monitors (e.g. one per team or per filter set). Letters, numbers, dashes and underscores.

## `maxConcurrency` (type: `integer`):

Upper limit of simultaneous HTTP requests. Requests to one host are additionally limited to 3 at a time to stay polite.

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

Timeout for a single HTTP request.

## `maxRetries` (type: `integer`):

Retries for network errors, HTTP 429 and 5xx responses (exponential backoff, honours Retry-After).

## `proxyConfiguration` (type: `object`):

Not needed for the public job-board feeds. Enable only if a site blocks your requests.

## Actor input object example

```json
{
  "companies": [
    "greenhouse:stripe",
    "https://jobs.lever.co/palantir",
    "ashby:openai",
    "workday:nvidia.wd5/NVIDIAExternalCareerSite",
    "smartrecruiters:ServiceNow"
  ],
  "keywords": [
    "engineer",
    "data scientist"
  ],
  "excludeKeywords": [
    "intern"
  ],
  "locations": [
    "San Francisco",
    "Remote"
  ],
  "departments": [
    "Engineering"
  ],
  "remoteOnly": false,
  "includeUnknownRemote": false,
  "postedWithinDays": 7,
  "includeDescription": true,
  "maxJobsPerCompany": 25,
  "trackChanges": false,
  "onlyNewAndClosed": false,
  "trackingId": "default",
  "maxConcurrency": 5,
  "requestTimeoutSecs": 60,
  "maxRetries": 4,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `jobs` (type: `string`):

All rows from this run as JSON (status is open, new, existing, closed or error).

## `summary` (type: `string`):

Per-company counts (jobs on board, pushed, new, closed) and any errors.

# 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://job-boards.greenhouse.io/stripe",
        "lever:palantir",
        "ashby:openai",
        "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite"
    ],
    "maxJobsPerCompany": 25
};

// Run the Actor and wait for it to finish
const run = await client.actor("fairfetch/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://job-boards.greenhouse.io/stripe",
        "lever:palantir",
        "ashby:openai",
        "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite",
    ],
    "maxJobsPerCompany": 25,
}

# Run the Actor and wait for it to finish
run = client.actor("fairfetch/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://job-boards.greenhouse.io/stripe",
    "lever:palantir",
    "ashby:openai",
    "https://nvidia.wd5.myworkdayjobs.com/NVIDIAExternalCareerSite"
  ],
  "maxJobsPerCompany": 25
}' |
apify call fairfetch/career-site-jobs-api --silent --output-dataset

```

## MCP server setup

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