# Career Site Job Scraper: Workday, Greenhouse, Lever & more (`vimplement/career-site-jobs`) Actor

Collect job postings from company career sites on 19 applicant tracking systems: Workday, Greenhouse, Lever, Ashby, SmartRecruiters, Teamtailor, Workable, Personio, BambooHR and more, plus the Nordic Jobylon, Varbi, ReachMee and EasyCruit. Clean normalized data, salaries, new-job monitoring.

- **URL**: https://apify.com/vimplement/career-site-jobs.md
- **Developed by:** [Victor Berg](https://apify.com/vimplement) (community)
- **Categories:** Jobs, Lead generation, Automation
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.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.

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: Greenhouse, Lever, Ashby & more

**Every open job from company career sites, as clean data, for $2 per 1,000 jobs.** Paste a job-board link or simply a company's careers page. The Actor reads the jobs from the company's applicant tracking system (ATS) and returns one normalized record per job: title, company, department, location with ISO country, remote / hybrid / on-site, employment type, seniority, dates, salary where published, and the full description.

**19 ATS:** Workday, Greenhouse, Lever, Ashby, SmartRecruiters, Recruitee, Workable, Personio, BambooHR, Breezy HR, Pinpoint, Rippling, JazzHR, Gem, and the Nordic Teamtailor, EasyCruit, Jobylon, Varbi and ReachMee.

**Built for**

- **Recruiters and sourcers** who watch a list of companies and want only the new openings each morning.
- **Job boards and aggregators** that fill a niche board (remote, data, Nordic tech, academia) straight from the source.
- **Lead generation and sales teams** looking for hiring signals, such as who is hiring data engineers in Oslo this month.
- **Labour-market analysts and researchers** comparing hiring by role, seniority, location, salary and remote policy in one schema.
- **AI agents** that need live job data: the Actor works as a tool through the [Apify MCP server](#use-it-from-an-ai-agent-mcp).

**Why this Actor**

- **No board URL needed.** Paste a company's careers page or website; the Actor finds the job board behind it.
- **One schema for 19 ATS**, so jobs from Workday and Teamtailor land in the same columns.
- **Pay only for matching jobs.** Title, location, remote, date and per-company filters run before anything is saved or charged.
- **Monitoring built in.** Schedule it and get only jobs you have not received before.
- **Fast and polite.** Plain HTTP against public job-board endpoints: no browser, no proxies, no login. Retries with backoff, at most 3 parallel requests per host, and one failing company never fails the run.
- **Privacy by default.** No recruiter or contact fields in the output, and e-mail addresses and phone numbers inside descriptions are removed.

### Supported applicant tracking systems

| ATS | Paste a URL like | Notes |
|---|---|---|
| **Greenhouse** | `https://boards.greenhouse.io/figma`, `https://job-boards.eu.greenhouse.io/jetbrains` | Salary ranges (pay transparency). US and EU boards. |
| **Lever** | `https://jobs.lever.co/spotify`, `https://jobs.eu.lever.co/lever` | Salary ranges. The EU instance is detected automatically. |
| **Ashby** | `https://jobs.ashbyhq.com/linear` | Compensation ranges and summary. |
| **Workday** | `https://adobe.wd5.myworkdayjobs.com/external_experienced` | The largest enterprise ATS. Newest jobs first. One extra request per job. |
| **SmartRecruiters** | `https://careers.smartrecruiters.com/BoschGroup` | Company IDs are case-sensitive. One extra request per job for the description. |
| **Recruitee** | `https://acme.recruitee.com` | Salary ranges. |
| **Workable** | `https://apply.workable.com/acme` | |
| **Personio** | `https://acme.jobs.personio.de` (or `.com`) | |
| **BambooHR** | `https://acme.bamboohr.com/careers` | One extra request per job. |
| **Breezy HR** | `https://acme.breezy.hr` | |
| **Pinpoint** | `https://acme.pinpointhq.com` | One extra request per job for the posting date. |
| **Rippling** | `https://ats.rippling.com/acme/jobs` | One extra request per job. |
| **JazzHR** | `https://acme.applytojob.com/apply` | One extra request per job. |
| **Gem** | `https://jobs.gem.com/acme` | |
| **Teamtailor** | `https://acme.teamtailor.com` or the company's own career domain | Widely used across the Nordics. |
| **EasyCruit** (Visma) | `https://acme.easycruit.com` | Common in Norway. One extra request per job for the description. |
| **Jobylon** | `https://emp.jobylon.com/companies/3142-lkab`, any Jobylon job link, or the company's careers page | Sweden, Finland, the Netherlands. One extra request per job for the date and full ad. |
| **Varbi** | `https://kth.varbi.com` | Swedish universities, agencies and municipalities. One extra request per job for the place and formatted ad. |
| **ReachMee** | The company's careers page, or `https://web103.reachmee.com/ext/I011/853/main?site=6&validator=...` | Swedish universities and government agencies. No posting date is published. One extra request per job. |

You can also paste a **company website or careers page** (e.g. `https://linear.app/careers`). The Actor loads that page and, if needed, the careers link on it (at most 2 page loads) and finds the embedded job board.

### Use cases

- **Daily new-job alerts for a target list.** Monitor 50 companies, run every morning, and push only new jobs to Slack, e-mail, Google Sheets or a webhook.
- **A niche job board.** Remote AI and ML roles at AI companies, or PhD and postdoc positions at Swedish universities, refreshed from the employers' own boards.
- **Hiring signals for B2B sales.** Which companies in Oslo posted data or AI roles in the last 30 days, with department and seniority to qualify the lead.
- **Salary benchmarking.** US product and engineering roles with the pay ranges that companies publish under pay-transparency laws.
- **Labour-market research.** Hiring volume by role, seniority, country and remote policy across hundreds of employers, in one consistent schema.
- **Public-sector and academic jobs in the Nordics.** Varbi, ReachMee and Jobylon cover many Swedish agencies, municipalities and universities that general job portals miss.

### How to use

1. Add **job-board URLs** or **careers pages** to *Job board or careers page URLs*, and/or pick the ATS and type the board name under *Companies by ATS and board name*.
2. Optionally set **filters** (title keywords, locations, remote only, posted within N days, max jobs per company). You only pay for jobs that match.
3. Click **Start**. Download the results as JSON, CSV, Excel or HTML, or use the API and integrations.

#### Input examples

Job boards and careers pages, filtered to data and engineering roles in Scandinavia:

```json
{
  "startUrls": [
    { "url": "https://boards.greenhouse.io/figma" },
    { "url": "https://jobs.lever.co/spotify" },
    { "url": "https://linear.app/careers" },
    { "url": "https://tibber.com" }
  ],
  "titleKeywords": ["engineer", "data"],
  "excludeTitleKeywords": ["intern"],
  "locations": ["Norway", "Sweden", "Remote"],
  "postedWithinDays": 30
}
```

Nordic public sector by ATS and board name (Varbi subdomain, ReachMee site id, Jobylon company number):

```json
{
  "companies": [
    { "ats": "varbi", "slug": "migrationsverket" },
    { "ats": "varbi", "slug": "kth" },
    { "ats": "reachmee", "slug": "I011-853-6", "name": "Linköpings universitet" },
    { "ats": "jobylon", "slug": "3142", "name": "LKAB" }
  ],
  "titleKeywords": ["IT", "utvecklare", "developer", "data"]
}
```

Daily monitoring of remote AI roles (save as a task and add a schedule):

```json
{
  "companies": [
    { "ats": "ashby", "slug": "cohere" },
    { "ats": "ashby", "slug": "elevenlabs" },
    { "ats": "ashby", "slug": "langchain" }
  ],
  "titleKeywords": ["AI", "ML", "machine learning", "LLM"],
  "remoteOnly": true,
  "onlyNewJobs": true,
  "monitoringId": "remote-ai-daily"
}
```

#### Input fields

| Field | Description |
|---|---|
| `startUrls` | Job-board URLs, careers pages or company websites (one company per line). |
| `companies` | `[{"ats": "greenhouse", "slug": "figma", "name": "Figma"}]`: ATS + board name; `name` is optional. Varbi uses the subdomain (`kth`), Jobylon the company number (`3142`), ReachMee `installation-customer-site` (`I011-853-6`), Workday `tenant.wdN/site`. |
| `descriptionFormat` | `text` (default, readable plain text), `html` (sanitized), `both`, or `none` (fastest). |
| `titleKeywords` / `excludeTitleKeywords` | Keep / drop jobs whose title contains any of the words (case- and accent-insensitive; terms up to 3 characters such as `AI` or `QA` match whole words). |
| `locations` | Keep jobs whose location matches any term: city, region or country, in English or the local language (`Oslo`, `Norway`, `Norge`, `NO`, `Remote`). |
| `remoteOnly` | Only fully remote jobs. |
| `postedWithinDays` | Only jobs posted in the last N days (falls back to the last-updated date; jobs without any date are dropped). |
| `maxJobsPerCompany` | Newest N jobs per company after filters. |
| `onlyNewJobs` + `monitoringId` | Only jobs not delivered in earlier runs of the same monitoring ID. |
| `redactContacts` | Remove e-mail addresses and phone numbers from descriptions (default on). |
| `maxConcurrency` | Parallel requests (default 5; never more than 3 per host). |

### Output

One dataset item per job. Example (description shortened):

```json
{
  "id": "lever:outreach:550bdb6a-59ce-4e8a-941b-17bcfc63b9a3",
  "atsJobId": "550bdb6a-59ce-4e8a-941b-17bcfc63b9a3",
  "ats": "lever",
  "company": "Outreach",
  "companySlug": "outreach",
  "title": "Senior Product Marketing Manager, Platform",
  "department": "Marketing",
  "team": "Product Marketing",
  "location": "United States",
  "locations": ["United States"],
  "country": "US",
  "countries": ["US"],
  "workplaceType": "remote",
  "isRemote": true,
  "employmentType": "full-time",
  "seniority": "senior",
  "postedAt": "2026-10-05T20:24:21Z",
  "updatedAt": null,
  "jobUrl": "https://jobs.lever.co/outreach/550bdb6a-59ce-4e8a-941b-17bcfc63b9a3",
  "applyUrl": "https://jobs.lever.co/outreach/550bdb6a-59ce-4e8a-941b-17bcfc63b9a3/apply",
  "salary": { "min": 135000, "max": 150000, "currency": "USD", "interval": "year", "text": null },
  "description": "About Outreach\n\nOutreach, founded in 2014, is the only complete agentic AI platform for revenue teams. ...",
  "sourceUrl": "https://jobs.lever.co/outreach",
  "scrapedAt": "2026-10-11T07:58:10Z"
}
```

| Field | Description |
|---|---|
| `id` | Stable unique ID `ats:companySlug:atsJobId`. Use it to de-duplicate across runs. |
| `atsJobId`, `ats`, `companySlug` | The job's ID in the ATS, the ATS name and the board name. |
| `company` | Company name as published by the ATS (falls back to the board name, or the name you gave in the input). |
| `title`, `department`, `team` | As published. `team` is filled by Lever, Ashby and Teamtailor. |
| `location`, `locations` | Primary location and all locations. |
| `country`, `countries` | ISO 3166-1 alpha-2 codes, from the ATS or parsed from the location text when unambiguous. |
| `workplaceType`, `isRemote` | `remote` / `hybrid` / `onsite` (from the ATS, else "Remote"/"Hybrid" in the location or title), or `null` when unknown. |
| `employmentType` | `full-time`, `part-time`, `contract`, `temporary`, `internship`, `volunteer` or `null`. |
| `seniority` | `intern`, `junior`, `mid`, `senior`, `lead`, `manager`, `director`, `executive`. A **heuristic**, see FAQ. |
| `postedAt`, `updatedAt` | ISO 8601 UTC, when the ATS provides them. |
| `jobUrl`, `applyUrl` | Public job page and application link. |
| `salary` | `{min, max, currency, interval, text}` when the ATS publishes a range (Greenhouse, Lever, Ashby, Recruitee and others where present), else `null`. |
| `description` | Plain text (paragraphs, `- ` bullets) or sanitized HTML; `descriptionHtml` is added with `both`. |
| `sourceUrl`, `scrapedAt` | The input URL the job came from (or the board URL), and the run time. |

Two dataset views are included: **Overview** and **Details & salary**. A run summary is saved to the **OUTPUT** record of the run's key-value store: companies processed, jobs found / matched / saved per company, errors per company, inputs that could not be resolved and the number of redacted contacts.

### Pricing

**Pay per event: $2.00 per 1,000 jobs** ($0.002 per job saved to the dataset).

- You pay only for jobs that pass your filters, because filtering happens before saving.
- With *Only new jobs*, jobs you already received are not saved again and not charged.
- Set a **maximum cost per run** when starting the Actor: it stops cleanly at the limit, and with monitoring on, jobs that did not fit are delivered in the next run.
- Apify adds its standard tiny *Actor start* event per run (by default $0.00005).

Examples: 100 companies with ~30 open jobs each ≈ 3,000 jobs ≈ **$6**. Monitoring 200 companies daily with ~50 new jobs a day ≈ **$0.10 per day**.

### Monitoring new jobs (scheduled runs)

1. Turn on **Only new jobs since previous runs** and give the configuration a **Monitoring ID** (e.g. `nordic-data-jobs`).
2. Save the input as a **Task** and add a **Schedule** (e.g. every morning).
3. Connect an integration (webhook, Slack, e-mail, Google Sheets, Zapier, Make) to receive each run's new jobs.

The first run returns all current jobs; later runs return only jobs that were not delivered before. Use a different monitoring ID for each saved configuration. The memory is kept in a named key-value store (`career-site-jobs-monitoring`) in your own Apify account.

### Use it from an AI agent (MCP)

Connect Claude, ChatGPT, Cursor or any other MCP client to the hosted Apify MCP server and add this Actor as a tool:

```
https://mcp.apify.com?tools=vimplement/career-site-jobs
```

Sign in with OAuth when the client asks, or send your Apify API token as a bearer token. The Actor then shows up as the tool `vimplement--career-site-jobs`; the agent passes the same input as above and reads the jobs from the run's dataset. Ask for example: *"List remote machine-learning roles at Cohere and ElevenLabs posted in the last week, with salary where published."* Agents that use the server's default tools can also find the Actor through Store search without pinning it.

### FAQ

**How do I find a company's job-board URL?** Open the company's careers page and click a job: the address usually shows the ATS (`boards.greenhouse.io/...`, `jobs.lever.co/...`, `jobs.ashbyhq.com/...`, `emp.jobylon.com/...`, `<name>.varbi.com`). Or paste the careers page itself and let the Actor find the board.

**How do I add a ReachMee or Jobylon company?** Paste the company's careers page: the Actor reads the embedded ReachMee job list or Jobylon widget from it. For Jobylon, any job link on `emp.jobylon.com` works too.

**Which Nordic job boards are covered?** Teamtailor, EasyCruit, Jobylon, Varbi and ReachMee, plus Personio, Recruitee, Workable and the EU instances of Greenhouse and Lever, which many Nordic companies use. Webcruiter is not supported: its robots.txt asks every crawler except Google's to stay away, and it has no documented public job feed.

**My careers page was not detected.** Some career pages load jobs with JavaScript from a source the page does not reveal, or use an ATS that is not supported yet. The run summary (OUTPUT) lists such inputs with the reason. Paste the job-board URL instead.

**Why are some fields empty?** The Actor never invents data. Greenhouse and Jobylon boards publish no employment type, ReachMee career sites publish no posting date, and salaries only appear when the company publishes a range.

**Why are ReachMee jobs missing when I filter by date?** ReachMee shows no posting date, and *Posted within the last N days* keeps only jobs with a known date. Leave that filter empty for ReachMee boards, or use *Only new jobs* to get just the new ones.

**How is seniority determined?** By rules on the job title (English plus Norwegian, Swedish, Danish and German terms such as *Senior*, *Lead*, *Head of*, *Praktikant*, *Avdelingsleder*), then the ATS's own experience level when the title has no marker; `mid` means no level was found. Individual-contributor titles such as *Product Manager* or *Account Manager* are not classed as people managers. Treat it as a strong hint, not a fact.

**How fresh is the data?** Live: every run reads the boards at that moment.

**Can I scrape Workday, LinkedIn or Indeed?** Workday career sites, yes: paste the career-site URL. LinkedIn and Indeed, no: this Actor covers public ATS job boards only.

**Is it gentle with the websites?** Yes: it reads the same public endpoints that power the companies' own career pages, with at most 3 parallel requests per host (2 for the full HTML job pages of Varbi, Jobylon and JazzHR), retries with exponential backoff on rate limits, and an identifying User-Agent.

### Limits

- Only the 19 ATS above. Not supported: iCIMS, Taleo, SuccessFactors, Webcruiter, and career pages that load jobs from a source they do not reveal.
- Fields are only as complete as the ATS publishes them (see *Why are some fields empty?*).
- ATS marked "one extra request per job" are slower on large boards. With `descriptionFormat: none` those requests are skipped unless a filter needs a detail.
- A careers page is followed for at most 2 page loads. If a board blocks automated requests or is down, that company is reported as failed in the run summary and the rest of the run continues.
- Contact redaction removes e-mail addresses and phone numbers; names written in the free text of an ad can remain.

### Legal and privacy

This Actor reads **publicly available job postings** from the public job-board endpoints (JSON APIs, RSS and XML feeds, and the job lists that companies embed on their career pages) that the ATS vendors provide for displaying jobs. It does not log in, bypass access controls or use proxies. It does not output personal data fields such as recruiter or hiring-manager names, e-mails or phone numbers, and by default it removes e-mail addresses and phone numbers that appear inside job descriptions (names written in free text can remain). You are responsible for using the data in line with the source websites' terms and applicable laws such as the GDPR. This Actor is not affiliated with or endorsed by any of the ATS vendors; all trademarks belong to their owners.

### Support

Found a board that does not work, or need another ATS? Open an issue on the Actor's **Issues** tab with the URL. Fixes and new ATS are prioritized from user requests.

# Changelog

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

# Actor input Schema

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

One URL per company. Use a job-board URL on a supported ATS (e.g. https://boards.greenhouse.io/figma, https://jobs.lever.co/spotify, https://jobs.ashbyhq.com/linear, https://apply.workable.com/acme, https://acme.teamtailor.com, https://acme.jobs.personio.de, https://acme.recruitee.com, https://careers.smartrecruiters.com/AcmeCorp, https://acme.easycruit.com) or simply the company's own careers page or website - the Actor then finds the embedded job board automatically (1-2 page loads per website). Also supported: Workday (https://acme.wd5.myworkdayjobs.com/External), BambooHR (https://acme.bamboohr.com), Breezy HR (https://acme.breezy.hr), Pinpoint (https://acme.pinpointhq.com), Rippling (https://ats.rippling.com/acme), JazzHR (https://acme.applytojob.com), Gem (https://jobs.gem.com/acme) and the Nordic ATS Varbi (https://kth.varbi.com), Jobylon (https://emp.jobylon.com/companies/3142-lkab, a Jobylon job link, or the company's careers page) and ReachMee (the company's careers page, or https://web103.reachmee.com/ext/I011/853/main?site=6\&validator=...).

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

Alternative to URLs: choose the ATS and enter the company's board name - the part after the domain, e.g. 'figma' for boards.greenhouse.io/figma, 'linear' for jobs.ashbyhq.com/linear, 'acme' for acme.teamtailor.com. SmartRecruiters uses the case-sensitive company id (e.g. 'BoschGroup'). The optional name replaces the company name in the output. Workday uses 'tenant.wdN/site' (e.g. 'adobe.wd5/external_experienced' for https://adobe.wd5.myworkdayjobs.com/external_experienced) - or simply paste the career-site URL. Varbi uses the subdomain ('kth' for kth.varbi.com), Jobylon the company number ('3142' for emp.jobylon.com/companies/3142-lkab) and ReachMee 'installation-customer-site' ('I011-853-6' for web103.reachmee.com/ext/I011/853/main?site=6).

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

Plain text is clean and readable (paragraphs and '- ' bullet points). HTML is sanitized (formatting tags only, no scripts or styles). 'None' skips descriptions: fastest, and for ATS that need one request per job (SmartRecruiters, EasyCruit, Workday, BambooHR, Pinpoint, Rippling, JazzHR, Varbi, Jobylon, ReachMee) it avoids those requests unless a filter needs a detail.

## `titleKeywords` (type: `array`):

Keep only jobs whose title contains at least one of these words or phrases (case-insensitive, accents ignored). Keywords of up to 3 characters such as 'AI', 'QA' or 'IT' match whole words only. Leave empty to keep all jobs.

## `excludeTitleKeywords` (type: `array`):

Drop jobs whose title contains any of these words or phrases (same matching rules as above).

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

Keep only jobs whose location matches one of these terms: a city, region or country as text (case-insensitive substring), e.g. 'Oslo', 'London', 'Remote'. Country names in English or the local language and ISO codes (e.g. 'Norway', 'Norge', 'NO', 'Sverige', 'UK') also match jobs in that country.

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

Keep only jobs marked as fully remote (by the ATS, or 'Remote' in the location). Hybrid jobs are excluded.

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

Keep only jobs published in the last N days (uses the posting date, or the last-updated date when the ATS has no posting date). Jobs without any date are dropped when this is set.

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

Save at most this many jobs per company (newest first, after filters). Leave empty for all jobs.

## `onlyNewJobs` (type: `boolean`):

Remember the jobs saved in earlier runs (in a named key-value store in your account) and save only jobs not seen before. The first run returns all current jobs. Only saved jobs are remembered, so changing filters can bring in older jobs that now match.

## `monitoringId` (type: `string`):

Name of the memory used by 'Only new jobs'. Use a different ID for each saved configuration or schedule (letters, digits, '-' and '\_'). Change it to start over.

## `redactContacts` (type: `boolean`):

Replace e-mail addresses and phone numbers inside job descriptions with '\[email redacted]' / '\[phone redacted]'. Recruiter names, e-mails and phone fields from the ATS are never included in the output either way.

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

How many requests may run at the same time (companies are processed in parallel). At most 3 requests run against the same host at once regardless of this value.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://job-boards.greenhouse.io/greenhouse"
    },
    {
      "url": "https://jobs.eu.lever.co/lever"
    },
    {
      "url": "https://career.teamtailor.com/jobs"
    }
  ],
  "companies": [
    {
      "ats": "workable",
      "slug": "careers",
      "name": "Workable"
    }
  ],
  "descriptionFormat": "text",
  "titleKeywords": [
    "engineer",
    "developer",
    "data"
  ],
  "excludeTitleKeywords": [
    "intern",
    "sales"
  ],
  "locations": [
    "Oslo",
    "Stockholm",
    "Remote"
  ],
  "remoteOnly": false,
  "onlyNewJobs": false,
  "monitoringId": "default",
  "redactContacts": true,
  "maxConcurrency": 5
}
```

# Actor output Schema

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

One item per job posting with the normalized fields (company, title, locations, country, workplace type, employment type, seniority, dates, URLs, salary, description).

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

OUTPUT record: companies processed, jobs found / matched / saved per company, errors per company and inputs that could not be resolved.

# 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/greenhouse"
        },
        {
            "url": "https://jobs.eu.lever.co/lever"
        },
        {
            "url": "https://career.teamtailor.com/jobs"
        }
    ],
    "companies": [
        {
            "ats": "workable",
            "slug": "careers",
            "name": "Workable"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("vimplement/career-site-jobs").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/greenhouse" },
        { "url": "https://jobs.eu.lever.co/lever" },
        { "url": "https://career.teamtailor.com/jobs" },
    ],
    "companies": [{
            "ats": "workable",
            "slug": "careers",
            "name": "Workable",
        }],
}

# Run the Actor and wait for it to finish
run = client.actor("vimplement/career-site-jobs").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/greenhouse"
    },
    {
      "url": "https://jobs.eu.lever.co/lever"
    },
    {
      "url": "https://career.teamtailor.com/jobs"
    }
  ],
  "companies": [
    {
      "ats": "workable",
      "slug": "careers",
      "name": "Workable"
    }
  ]
}' |
apify call vimplement/career-site-jobs --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,vimplement/career-site-jobs"
        }
    }
}
```

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/Hf4mgS5L4D0FDXUdd/builds/mAh59kFLJbacXhGD2/openapi.json
