Workable Jobs Scraper — Search 170K Jobs, No Slug
Pricing
from $2.00 / 1,000 job results
Workable Jobs Scraper — Search 170K Jobs, No Slug
Search all ~170,000 live Workable jobs by keyword, location, workplace type and recency — no company slug needed — or scrape specific company boards. Multi-country postings are billed once, not once per country. Delta monitoring, BYOK AI enrichment, MCP-ready.
Pricing
from $2.00 / 1,000 job results
Rating
0.0
(0)
Developer
Nomad.Dev
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
8 days ago
Last modified
Categories
Share
Search every job on Workable by keyword — you do not need to know a company slug. Workable's public index carries roughly 170,000 live postings from tens of thousands of SMBs and scale-ups; this Actor searches all of it, or scrapes specific company boards directly if you already know who you want.
No login, no API key, no proxies. Public APIs only.
{ "searchQuery": "python engineer", "searchWorkplace": "remote", "searchPostedDays": 7 }
That's a complete run. No slug hunting.
Why this one
Most Workable scrapers make you supply a company slug — so you can't use them until you already know which companies you want. The ones that do search have a quieter problem, and it's on your invoice.
Workable's index lists a posting once per country it's open in — each copy with its own id and URL, all the same job. One posting in a recent run was listed in 36 countries. A scraper that reads the index literally returns that job 36 times and bills you 36 times.
This Actor folds them back into one row per real job, every country in a locations array. Measured on a live run: 46 jobs returned, 46 results billed — those 46 jobs occupy 363 rows in Workable's index. Billed per row, the same jobs cost 7.9× more. On a broad unfiltered search, roughly a third of index rows are duplicates; on a narrow one it's far more.
| This Actor | |
|---|---|
| Search all of Workable, no slug | ✅ |
| Scrape specific company boards | ✅ (batch, many at once) |
| One row per job, not per country | ✅ — the 7.9× above |
workplace (remote / hybrid / on-site) | ✅ stated by Workable, not guessed |
| Full description + requirements + benefits | ✅ |
| Company website (lead-gen) | ✅ on every row |
| Delta mode — only pay for new postings | ✅ |
| AI enrichment (skills, seniority, salary) | ✅ opt-in, bring your own key |
Two ways to run it
1. Search (no slug needed) — the whole Workable index:
{"searchQuery": "registered nurse","searchLocation": "London","searchWorkplace": "hybrid","searchPostedDays": 14,"maxItems": 100}
2. Company boards — when you already know the companies:
{ "companies": ["netguru", "typeform"], "keyword": "engineer" }
Find a slug by opening the company's careers page and reading it out of apply.workable.com/<slug>/. Full board URLs work too — they're reduced to the slug for you. You can combine both modes in one run; a posting found by both is returned (and billed) once.
Input
| Field | Type | Default | Description |
|---|---|---|---|
searchQuery | string | — | Free-text search across every job on Workable — no company slug needed. e.g. python engineer. |
searchLocation | string | — | City, region or country to search in, e.g. London, Germany. |
searchWorkplace | string | any | any / remote / hybrid / on_site. An exact filter — Workable states this per posting. |
searchPostedDays | integer | — | Only search postings published in the last N days. |
companies | array | — | Optional. Workable board slugs (full board URLs also accepted). |
keyword | string | — | Case-insensitive substring match on the job title. Applies to both modes. |
titleExclude | array | — | Drop postings whose title contains any of these substrings. |
locationFilter | string | — | Case-insensitive substring match on the location. |
postedSince | integer | — | Keep only postings published within N days. Postings with no date are dropped when set. |
remoteOnly | boolean | false | Fully-remote only. Hybrid is not counted as remote — use searchWorkplace for that. |
includeDescription | boolean | true | Include the full plain-text description. Turn off for lighter output. |
maxItemsPerCompany | integer | 100 | Cap postings per company board (0 = no cap). Each result is billed. |
maxItems | integer | 200 | Hard cap on total postings returned (0 = no cap). Each result is billed. In search mode this also bounds how deep the Actor pages. |
onlyNewSinceLastRun | boolean | false | Delta/monitoring mode — see below. |
aiEnrichment | boolean | false | Opt-in BYOK AI fields — see below. |
aiProvider | string | anthropic | anthropic or mistral. |
anthropicApiKey | string (secret) | — | Your Anthropic key. Billed by Anthropic, not by this Actor. |
aiModel | string | claude-haiku-4-5-20251001 | claude-haiku-4-5-20251001 (fast/cheap) or claude-sonnet-4-5. |
mistralApiKey | string (secret) | — | Your Mistral key. Billed by Mistral, not by this Actor. |
mistralModel | string | mistral-small-latest | mistral-small-latest / mistral-medium-latest / mistral-large-latest. |
concurrency | integer | 8 | Company boards fetched in parallel (advanced). |
Output
One flat JSON record per real job (not per index row):
| Field | Meaning |
|---|---|
title | Job title as posted |
company | Company display name |
companyWebsite | Company website — present on every search result, useful for lead-gen |
companyLogo | Company logo URL (company-board mode) |
location | Primary location text |
locations | Every location the posting is open in — the countries a naive scraper would bill you for separately |
locationCity / locationRegion / locationCountry | Structured location, straight from Workable |
workplace | remote / hybrid / on_site — stated by Workable, never inferred (search mode) |
remote | true only for fully-remote. Hybrid is false. null when Workable states neither |
employmentType | Full-time / Part-time / Contract etc. where stated |
department | Department or team where provided |
snippet | Full plain-text description |
requirements | Requirements section, where Workable states one separately |
benefits | Benefits section, where Workable states one separately |
industry | Company industry (company-board mode) |
experience / education | Experience level and education requirement, where stated (company-board mode) |
postedAt | First-published date (YYYY-MM-DD) |
url | Link to the posting |
applyUrl | Direct link to the application form |
id | Workable's own posting id |
globalId | Stable composite id — the delta-mode dedupe key |
warnings | Data-quality notes for this record (empty array when there's nothing to flag) |
isNew | Only when onlyNewSinceLastRun is on — always true |
ai* | Only when aiEnrichment is on — see below |
Call it from code
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("nomad-agent/workable-jobs-scraper").call(run_input={"searchQuery": "python engineer","searchWorkplace": "remote","maxItems": 50,})for item in client.dataset(run["defaultDatasetId"]).iterate_items():print(item["company"], "|", item["title"], "|", item["workplace"], item["url"])
curl -X POST \"https://api.apify.com/v2/acts/nomad-agent~workable-jobs-scraper/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \-H "Content-Type: application/json" \-d '{"searchQuery": "python engineer", "searchWorkplace": "remote"}'
Output example
{"ats": "workable","company": "payabl.","companyWebsite": "http://payabl.com","id": "5b10e8f7-627e-4cd3-8f09-3c40c96f71f5","title": "Senior Backend Python Engineer (Core Gateway)","department": "Engineering","location": "Czechia","locations": ["Czechia", "Germany", "United Kingdom", "Romania", "Serbia", "Netherlands"],"locationCountry": "Czechia","url": "https://jobs.workable.com/view/cfdWh7wNW4V1G8zXPmvN1n/...","applyUrl": "https://jobs.workable.com/view/cfdWh7wNW4V1G8zXPmvN1n/...","postedAt": "2026-06-22","employmentType": "Full-time","workplace": "remote","remote": true,"snippet": "About payabl. We are a fast-growing fintech...","requirements": "5+ years of Python...","benefits": "Private health insurance...","globalId": "workable:search:5b10e8f7-627e-4cd3-8f09-3c40c96f71f5","warnings": []}
That is one row. A scraper that reads Workable's index literally would have returned it six times — once per country — and billed you six times.
Delta mode / monitoring
Set onlyNewSinceLastRun: true to turn this Actor into a "what's new" monitor. Postings already seen on a previous run made with this flag on are dropped before push — you are not billed for them. Pair it with an Apify schedule (cron) and every run only returns, and only charges for, postings that appeared since the last flagged run.
Seen postings are tracked in a dedicated key-value store keyed by globalId, capped at ~50,000 entries (oldest evicted first). The first flagged run has nothing to compare against, so it emits everything. Runs with the flag off never read or write this cache — toggling it between runs is safe.
Delta mode is applied while the Actor pages, not after: a search that has to page deep to find new postings keeps paging until it has them, rather than handing back an empty run.
AI enrichment
Turn on aiEnrichment and supply your own anthropicApiKey (or mistralApiKey with aiProvider: "mistral") to add AI-extracted fields to every posting:
| Field | Meaning |
|---|---|
aiKeySkills | Skills/technologies explicitly named in the posting — never invented |
aiExperienceLevel | entry / mid / senior / lead / unknown |
aiWorkArrangement | onsite / hybrid / remote / unknown |
aiVisaSponsorship | true/false only when the posting states a policy, else null |
aiSalaryRange | {min, max, currency, period} only from explicitly stated pay figures — never inferred |
The prompt is explicit about never guessing: when the text doesn't support a value you get unknown / null / an empty array, not a fabrication.
You usually don't need AI for work arrangement — search mode returns Workable's own workplace field for free, and it's authoritative rather than inferred.
Postings are batched (~12 per call). Your key is billed by that provider, not by this Actor. Rough cost with Haiku or Mistral Small: ~100 postings well under $0.05 in provider tokens; Sonnet/Mistral Large roughly 4-5× that.
If aiEnrichment is on but no matching key is available, enrichment is skipped: you get one dataset row explaining why, and every posting is still returned normally without the ai* fields.
Warning rows
Alongside normal postings, this Actor can push rows that are all null except company/ats/warnings — same flat schema, so a dataset-only consumer sees every problem without reading the run log:
| Trigger | Example warnings |
|---|---|
| A requested company slug 404'd | ["not found on any ATS probed (workable): 'acme-corp' (404 on every probe)"] |
maxItemsPerCompany cut postings for one company | ["truncated for 'acme-corp': maxItemsPerCompany=100 returned 100 of 137 matching postings"] |
maxItems cut the run | ["truncated: maxItems=200 returned 200 of 3401 matching postings"] |
aiEnrichment requested with no key | ["aiEnrichment skipped: no anthropicApiKey or mistralApiKey provided"] |
None of these rows are billed — only genuine postings are.
Integrations
Export as JSON, CSV or Excel, or pipe into Make, Zapier or n8n. Call it synchronously with run-sync-get-dataset-items, or plug it into any AI agent through the Apify MCP server.
Pricing
Pay per event — the current rate is shown in the Pricing section of this page. No subscription, no minimum.
Compare on cost per real job, not cost per row. Workable's index repeats a multi-country posting once per country, and a scraper that bills per index row bills you for every one of them. A real run of this Actor: 46 jobs returned, 46 results billed — but those 46 jobs occupy 363 rows in Workable's index (one posting alone is open in 36 countries). Billed per row, the same 46 jobs cost 7.9× more.
So a cheaper-looking headline rate is often the more expensive scraper.
If you turn on aiEnrichment, your Anthropic/Mistral key is billed separately by that provider. Delta mode only reduces cost — already-seen postings are dropped before the billed push.
Use cases
- Find who's hiring for a role across all of Workable, without a company list
- Recruiting-agency lead generation (company website on every row)
- Job-board aggregation and "new postings" alerting (delta mode + cron)
- Hiring/market research on SMBs and scale-ups (Workable is strong in Europe)
FAQ
Do I need a company slug?
No. searchQuery searches every company on Workable. Slugs are optional, for when you want specific boards.
Is it legal to scrape Workable jobs? The data comes from Workable's official, public, unauthenticated JSON APIs — the same data any visitor sees on a careers page. Review the terms for your use case.
Do I need an API key or login? No. Public APIs, no authentication of any kind.
Why does one job list several countries?
Because it's open in all of them. Workable's index repeats such a posting once per country; this Actor returns it once with every country in locations, so you aren't billed repeatedly for the same job.
How fresh is the data? Every run hits Workable live. No caching layer in between.
Something broken or missing? Open an issue on the Issues tab — it's monitored and fixes ship fast.
Related Actors
- Greenhouse Jobs Scraper — Company Job Boards
- Lever Jobs Scraper — Company Job Postings API
- Ashby Jobs Scraper — Company Job Boards API
- Company Careers Scraper — Greenhouse Lever Ashby
- LinkedIn Jobs Scraper — No Login, No Cookies
From the maker of Oink — an open-source, AI-powered job-search bot for Telegram that runs on these Actors. Try the free bot, get a managed instance at oinkjobsearch.com, or browse the full catalog of 50+ Actors.