# Workable Jobs Scraper (`khadinakbar/workable-jobs-scraper`) Actor

Extract current public Workable job boards by company slug or careers URL. Returns validated job records with titles, locations, departments, salaries, descriptions, application URLs, and source provenance.

- **URL**: https://apify.com/khadinakbar/workable-jobs-scraper.md
- **Developed by:** [Khadin Akbar](https://apify.com/khadinakbar) (community)
- **Categories:** Jobs, Lead generation, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 validated workable job returneds

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/platform/actors/running/actors-in-store#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

## Workable Jobs Scraper

Workable Jobs Scraper returns one structured dataset record for each current public job opening on a specified Workable company board. Give it a company slug or public `apply.workable.com` URL to collect titles, departments, locations, salary text, workplace type, optional description-level content, application URLs, and source URLs for traceable recruiting or job-market analysis.

### Best fit

- Use this actor when you already know employers that use Workable and need their current public openings in a consistent dataset.
- Pass one or more employer slugs or Workable careers URLs, then use title, location, or department filters before a record is written and billed.
- This focused public-board workflow complements dedicated tools for employer discovery, candidate records, applications, and interview data.

### Focused standalone workflow

This actor is designed as a focused standalone workflow for teams that already have a selected employer list and need a source-linked Workable job dataset. It works as a focused standalone workflow because each call starts with explicit public company boards, applies filters before the dataset write, and preserves the listing and detail sources alongside the result.

### From employer list to recruiting signal

A talent-intelligence team maintains a list of competitors with Workable career boards. They pass those employers with an engineering and remote filter. The actor returns `title`, `location`, `department`, `workplaceType`, `postedAt`, `applyUrl`, and source provenance; the team can compare role mix, keep a current hiring watchlist, or route an opening into a job-matching workflow.

### Quick start

```json
{
  "companies": ["recurly", "https://apply.workable.com/platzi/"],
  "maxJobsPerCompany": 25,
  "titleIncludes": "engineer",
  "locationIncludes": "remote",
  "departmentIncludes": "engineering",
  "includeDescriptions": true
}
```

`companies` accepts raw Workable slugs and public Workable careers URLs. `maxJobsPerCompany` is a per-employer collection boundary; the actor deliberately uses Workable's bounded public listing route rather than making unbounded requests.

### What data you receive

Every dataset row is one public job post. The fields keep source and collection context beside the business data.

| Field | Meaning |
| --- | --- |
| `jobId`, `companySlug`, `companyName` | Public job identity and employer context. |
| `title`, `location`, `department`, `employmentType` | Role fields for filtering and analysis. |
| `workplaceType`, `salary`, `postedAt` | Public work-model, compensation, and freshness signals when exposed. |
| `descriptionText`, `requirementsText`, `benefitsText` | Detail-page content when enabled. |
| `applyUrl`, `jobUrl`, `sourceListingUrl`, `scrapedAt` | Direct application path and provenance. |

```json
{
  "jobId": "54B48C70F0",
  "title": "Staff Software Engineer",
  "companyName": "Recurly",
  "companySlug": "recurly",
  "location": "Medellín, Colombia (Hybrid)",
  "department": "Engineering",
  "employmentType": "Full-time",
  "workplaceType": "hybrid",
  "descriptionText": "We're looking for a Staff Full-Stack Engineer...",
  "applyUrl": "https://apply.workable.com/recurly/j/54B48C70F0/apply",
  "jobUrl": "https://apply.workable.com/recurly/jobs/view/54B48C70F0.md",
  "sourceDetailUrl": "https://apply.workable.com/recurly/jobs/view/54B48C70F0.md",
  "sourceListingUrl": "https://apply.workable.com/recurly/jobs.md?query=a",
  "postedAt": null
}
```

### API and agent use

```bash
curl -X POST "https://api.apify.com/v2/acts/khadinakbar~workable-jobs-scraper/runs" \
  -H "Authorization: Bearer YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"companies":["recurly"],"maxJobsPerCompany":25,"includeDescriptions":true}'
```

For an AI agent workflow through Apify MCP, use this prompt:

> Collect up to 25 public engineering jobs from the Workable company board `recurly`. Return title, location, department, workplace type, direct application URL, posted timestamp, and source URLs. Preserve the actor outcome and provenance in the final analysis.

The actor is read-only. It returns its data in the default dataset, a compact `OUTPUT` contract, and a detailed `RUN_SUMMARY` diagnostic record. `INVALID_INPUT` and valid empty matches succeed truthfully; a completely inaccessible board fails as `UPSTREAM_FAILED`, while useful partial records are retained as `PARTIAL`.

### Pricing and boundaries

This actor uses Pay per event plus Apify platform usage. A validated job written to the dataset costs **$0.004 per job**; filtering happens before writing and no record is charged before it is persisted. The actor-start event is **$0.00005**. For a bounded 25-record run, event charges are at most **25 records × $0.004 + $0.00005 = $0.10005**, plus Apify platform usage. Workable's public listing route is bounded, and `maxJobsPerCompany` makes the collection and event-cost ceiling explicit. The live Pricing tab is the source of truth for current event pricing and platform-usage details.

### Limits and recovery

The public Workable Markdown listing route returns a bounded current listing, so each company is capped at 30 returned records and no unverified pagination is attempted. If a board is temporarily unavailable, retry later with the same public company slug; if a detail page is unavailable after a listing is collected, the actor preserves the useful listing fields and reports a `PARTIAL` outcome. Use the `OUTPUT` and `RUN_SUMMARY` records to distinguish a valid empty filter result from an unavailable public board.

### Freshness and provenance

The implementation uses Workable's public `jobs.md` listing and matching job-detail Markdown routes. `sourceListingUrl`, `sourceDetailUrl`, `postedAt`, and `scrapedAt` are persisted so downstream users can verify the source and collection time for every job record.

### Best results

- Start from selected employers that visibly host their careers board on Workable.
- Set `includeDescriptions` to false for a compact role inventory; keep it on when requirements and benefit text matter.
- Preserve `sourceListingUrl`, `sourceDetailUrl`, and `scrapedAt` in downstream reporting so others can verify the public record.
- Schedule a focused repeat collection for hiring monitoring and compare `postedAt` with `scrapedAt` rather than treating a single run as a permanent record.

### Builder's note

I designed this actor around Workable's public Markdown routes because the listing and job-detail responses are stable, compact, and source-addressable. Keeping a listing source, detail source, direct application URL, and machine-readable run outcome in the same contract makes the data useful to both recruiters and agents without exposing private ATS data.

### Responsible use

Use publicly available or authorized data in accordance with applicable laws, source terms, and your organization's data-governance policies. Keep source URLs with shared records so recipients can review the employer's current public posting.

# Actor input Schema

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

Enter one Workable company slug or public careers URL per line, such as recurly or https://apply.workable.com/recurly/. The actor extracts up to 25 unique slugs. This is required and is not an arbitrary company website, login page, individual application form, or private ATS URL.

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

Caps validated job records returned for each Workable company board. Enter an integer such as 25. It defaults to 30 and accepts 1 through 30 because Workable's current public Markdown listing route returns a bounded set. This is a per-company result cap, not a page count or global collection budget.

## `searchQuery` (type: `string`):

Optional text passed to Workable's public jobs listing route, for example engineer. Use it to narrow a large board before extraction. Leave blank to request the board's current public listing. This is not a Boolean expression or a cross-company Workable search.

## `titleIncludes` (type: `string`):

Optionally retain only jobs whose public title contains this text, such as engineer. Matching is case-insensitive literal text and happens before a dataset record is written or billed. Leave blank to keep all titles. This does not search private application data or description text.

## `locationIncludes` (type: `string`):

Optionally retain only jobs whose public Workable location contains this text, such as remote or london. Matching is case-insensitive literal text against the board's location column. Leave blank to keep all locations. This does not geocode roles or search office addresses.

## `departmentIncludes` (type: `string`):

Optionally retain only jobs whose public Workable department contains this text, such as engineering. Matching is case-insensitive literal text and occurs before write and billing. Leave blank to keep all departments. This does not infer departments from a description.

## `includeDescriptions` (type: `boolean`):

Controls whether the actor reads each matched public Workable job detail route for description, requirements, benefits, workplace type, and apply URL. Set false for a smaller, faster listing-level dataset. It defaults to true. This does not access candidates, hiring notes, or application answers.

## Actor input object example

```json
{
  "companies": [
    "recurly",
    "https://apply.workable.com/platzi/"
  ],
  "maxJobsPerCompany": 25,
  "searchQuery": "engineer",
  "titleIncludes": "engineer",
  "locationIncludes": "remote",
  "departmentIncludes": "engineering",
  "includeDescriptions": true
}
```

# Actor output Schema

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

Validated public Workable job records returned by this run.

## `output` (type: `string`):

Compact outcome, item counts, warnings, and charged event totals.

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

Detailed board coverage, source diagnostics, and billing information for this run.

# 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": [
        "recurly"
    ],
    "maxJobsPerCompany": 30,
    "searchQuery": "",
    "titleIncludes": "",
    "locationIncludes": "",
    "departmentIncludes": "",
    "includeDescriptions": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("khadinakbar/workable-jobs-scraper").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": ["recurly"],
    "maxJobsPerCompany": 30,
    "searchQuery": "",
    "titleIncludes": "",
    "locationIncludes": "",
    "departmentIncludes": "",
    "includeDescriptions": True,
}

# Run the Actor and wait for it to finish
run = client.actor("khadinakbar/workable-jobs-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).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": [
    "recurly"
  ],
  "maxJobsPerCompany": 30,
  "searchQuery": "",
  "titleIncludes": "",
  "locationIncludes": "",
  "departmentIncludes": "",
  "includeDescriptions": true
}' |
apify call khadinakbar/workable-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=khadinakbar/workable-jobs-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/cetk6UriLHVw6AIVY/builds/I0gfwadKnYLCPoiNC/openapi.json
