Workable Jobs by Company: ATS Career Page Postings API avatar

Workable Jobs by Company: ATS Career Page Postings API

Pricing

from $1.05 / 1,000 job row returneds

Go to Apify Store
Workable Jobs by Company: ATS Career Page Postings API

Workable Jobs by Company: ATS Career Page Postings API

Published jobs of any company hosted on Workable, read through the account's public jobs endpoint: title, department, function, city, state, country, remote flag, employment type, publish date, apply link and optional description. Two extra modes count open jobs per department and per location.

Pricing

from $1.05 / 1,000 job row returneds

Rating

0.0

(0)

Developer

Samat Makatov

Samat Makatov

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

5 days ago

Last modified

Share

Published jobs of any company hosted on Workable, read through the account's public jobs endpoint: title, department, function, city, state, country, remote flag, employment type, publish date, apply link and optional description. Two extra modes count open jobs per department and per location.

Give it the slugs of the career pages you care about (devsinc-17, prox-works, wantable-careers) and every published opening of those companies comes back as a clean row — company, job title, department, city, region, country, ISO country code, remote flag, employment type, seniority, education, job function, industry, publish date, days online, apply URL, and the full job description on request. The rows come from the employer's own career page, so the company behind a posting is never a guess. Built for recruiters and sourcing agencies watching a list of accounts, for go-to-market teams who treat a new opening as a buying signal, and for job-market researchers. No API key, no login, no browser, no proxy: one keyless request per career page.

Use cases

  • Sourcing watchlist: keep the openings of 20–200 target employers in one dataset, refreshed on a schedule; onlyNew writes only the postings that appeared since the last run.
  • Hiring signal for B2B sales: postedWithinDays: 30 over your account list shows who started hiring — a team that grows is a team with a budget.
  • One job family across many employers: titleKeywords: ["engineer"] plus excludeTitleKeywords: ["intern"] turns a list of career pages into a role-specific feed.
  • Remote-only job feed: workplace: "remote" keeps the postings employers marked as remote, ready for a newsletter or a job board.
  • AI screening and keyword audits: includeDescription: true adds the description as HTML and as plain text, so an LLM can score a posting against a profile without a second fetch.
  • Org and market research: mode: "departments" shows which teams a company is growing, mode: "locations" which countries it hires in — one row per department or country with its open-job count.

Input

FieldTypeDefaultAllowed values / notes
accountsstring[]requiredCareer page slugs, e.g. ["devsinc-17", "prox-works"]. A whole career page URL works too (https://apply.workable.com/devsinc-17/); a link to a single job (/j/<code>) is rejected with a message. See Finding the slug.
modestringjobsjobs, departments, locations — see Modes
titleKeywordsstring[]emptyKeep a job when its title contains any of these (case-insensitive)
excludeTitleKeywordsstring[]emptyDrop a job when its title contains any of these; applied after titleKeywords
departmentsstring[]emptyKeep jobs whose department contains any of these. Free text per company — mode: "departments" prints the exact list
locationContainsstring[]emptyText match over city, region and country of every location of a job
countryCodesstring[]emptyExact two-letter ISO 3166-1 codes, e.g. ["US", "GB", "PK"]; mode: "locations" prints the codes a page uses
workplacestringanyany, remote, onsite — from the employer's own remote flag
employmentTypesstring[]empty (all)Employment types; setting it drops postings where the employer left the field blank
experienceLevelsstring[]empty (all)Experience levels; same rule about blank values
postedWithinDaysintegerempty (no limit)1–3650. Published in the last N days; the source's publish date has day resolution
onlyNewbooleanfalseWrite only postings that were not delivered by an earlier run of this actor (jobs mode only)
includeDescriptionbooleanfalseAdd descriptionHtml and descriptionText; same request, no extra source call
sortBystringpublishedDescpublishedDesc, publishedAsc, titleAsc, departmentAsc, pageOrder — see Sort orders
maxItemsinteger501–5000 rows in total, across all career pages
maxItemsPerAccountintegerempty (no cap)1–2000 rows per career page, applied before maxItems
fieldsstring[]allKeep only these output fields, in this order

Reference

Finding the slug

A hosted career page looks like https://apply.workable.com/<slug>/ — the <slug> part is what this actor takes: apply.workable.com/devsinc-17/ → devsinc-17. Companies usually link that page from their own site as "Careers" or "Open positions"; the slug is frequently not the company name (wantable-careers for Wantable, prox-works for Proximity Works). You may paste the whole URL and the slug is read from it, in any letter case. A link to one posting (apply.workable.com/j/F9263E6A0E) contains no slug and is refused with a message instead of being guessed at. A slug the platform does not serve comes back as one row with found: false and an explanation, and the run still succeeds.

Modes

modeOne row perFields you get
jobspublished jobthe full job row below; all filters apply
departmentsdepartment of the career pagename, openJobs, filterUrl, facetType: "department"
locationslocation of the career pagename, locationCode (ISO 3166-1 alpha-2), openJobs, facetType: "location"

The dictionary modes ignore the job filters — they describe a whole career page. They are the cheapest way to learn the values departments and countryCodes expect, and a product in themselves: which teams is this employer growing, in which countries does it hire.

Employment types

Full-time, Part-time, Contract, Temporary, Internship, Other. This is a closed list: the input schema rejects anything else before the run starts, and the actor reads the values you pass in any letter case (full-time, FULL-TIME). The field is optional for the employer: on small career pages 10–50 % of postings leave it blank, and those are dropped as soon as you set the filter.

Experience levels

Internship, Entry level, Associate, Mid-Senior level, Director, Executive, Not Applicable. A closed list and employer-optional, exactly like the employment types above.

Sort orders

sortByOrder
publishedDescnewest publish date first (default)
publishedAscoldest publish date first
titleAscjob title A–Z
departmentAscdepartment A–Z, then title
pageOrderexactly as the career page returns the postings

Rows are ordered across all career pages of a run, after maxItemsPerAccount was applied, so maxItems keeps the newest postings of the whole watchlist. In the dictionary modes rows are ordered by open-job count, largest first.

Filter semantics

Filters are combined with AND, values inside one filter with OR: titleKeywords: ["engineer", "designer"] with countryCodes: ["US"] means "engineer or designer, in the United States". Filtering happens on the complete response of each career page, so nothing is lost to paging. countryCodes compares the code of the job's own location record; locationContains is a text match over city, region and country, including the extra locations of a multi-location posting. A job without a department cannot satisfy a departments filter and is dropped. Because department names are free text each company types itself, a misspelt one is matched against the names that career page really uses (Enginering → Engineering, written to the log); a value that resembles nothing is kept as typed and the log prints the page's own list. Either way a filter returns fewer rows — it never widens the search.

Examples

Every opening of one employer, newest first

{ "accounts": ["devsinc-17"], "mode": "jobs", "maxItems": 25 }

Remote-only feed from several career pages

{ "accounts": ["remote-recruitment", "remotebase"], "mode": "jobs", "workplace": "remote", "maxItems": 25 }

Engineering roles across a sourcing watchlist

{ "accounts": ["prox-works", "devsinc-17", "remotebase"], "mode": "jobs", "titleKeywords": ["engineer"], "excludeTitleKeywords": ["intern"], "maxItems": 20 }

New postings as a hiring signal (last 30 days)

{ "accounts": ["remote-recruitment", "devsinc-17", "prox-works"], "mode": "jobs", "postedWithinDays": 30, "sortBy": "publishedDesc", "maxItems": 25 }

Full-time United States jobs only

{ "accounts": ["aetos-systems-inc", "wantable-careers", "scalable"], "mode": "jobs", "countryCodes": ["US"], "employmentTypes": ["Full-time"], "maxItems": 25 }

Job descriptions for an LLM screen

{ "accounts": ["wantable-careers"], "mode": "jobs", "includeDescription": true, "maxItems": 5 }

Which teams is this company growing

{ "accounts": ["devsinc-17", "prox-works"], "mode": "departments", "maxItems": 30 }

Where does it hire

{ "accounts": ["remote-recruitment", "prox-works"], "mode": "locations", "maxItems": 30 }

Output

One real row of a cloud run (input: {"accounts": ["devsinc-17"], "mode": "jobs", "maxItems": 25}):

{
"account": "devsinc-17",
"companyName": "Devsinc",
"title": "Functional Consultant",
"shortcode": "3F21775CBA",
"requisitionCode": null,
"department": "Cluster Head",
"function": "Consulting",
"industry": "Computer Hardware",
"experienceLevel": "Mid-Senior level",
"educationLevel": "Bachelor's Degree",
"employmentType": "Full-time",
"isRemote": false,
"city": "Lahore",
"region": "Punjab",
"country": "Pakistan",
"countryCode": "PK",
"locationText": "Lahore, Punjab, Pakistan",
"extraLocations": [],
"locationCount": 1,
"publishedOn": "2026-09-25",
"createdOn": "2024-11-28",
"daysSincePublished": 1,
"url": "https://apply.workable.com/j/3F21775CBA",
"applyUrl": "https://apply.workable.com/j/3F21775CBA/apply",
"careerPageUrl": "https://apply.workable.com/devsinc-17/",
"found": true,
"error": null,
"fetchedAt": "2026-09-26T20:47:46.177Z"
}
FieldTypeMeaning
accountstringCareer page slug that produced the row (always filled)
companyNamestringCompany name as the career page publishes it (always filled)
titlestringJob title (always filled)
shortcodestringThe platform's code for the posting, also the last part of url (always filled)
requisitionCodestring | nullThe employer's internal code — optional, often empty
departmentstring | nullFree-text team name; empty on career pages that use none
functionstring | nullJob function, e.g. Art/Creative, Consulting — employer-optional
industrystring | nullIndustry of the employer as chosen for the posting — employer-optional
experienceLevelstring | nullSeniority from the dictionary — employer-optional
educationLevelstring | nullRequired education, e.g. Bachelor's Degree — employer-optional
employmentTypestring | nullEmployment type — employer-optional
isRemotebooleanThe employer's remote flag (always present; one flag, no hybrid value)
citystring | nullCity of the first location; empty for country-wide postings
regionstring | nullState, province or region of the first location
countrystring | nullCountry name of the first location
countryCodestring | nullISO 3166-1 alpha-2 code of the first location — what countryCodes filters on
locationTextstring | nullCity, Region, Country of the first location, skipping what is empty
extraLocationsstring[]The same text for every further location of the posting (empty for single-location jobs)
locationCountnumberHow many locations the posting lists
publishedOnstring | nullPublish date, YYYY-MM-DD (the source gives no clock time)
createdOnstring | nullDate the posting was created in the system; can be much older than publishedOn
daysSincePublishednumber | nullWhole UTC days since publishedOn — what postedWithinDays filters on
urlstringPublic job page (apply.workable.com/j/<shortcode>)
applyUrlstring | nullThe posting's application page
careerPageUrlstringThe company's career page
descriptionHtmlstring | nullJob description as the employer published it — only with includeDescription
descriptionTextstring | nullThe same text with tags removed and entities decoded — only with includeDescription
foundbooleanfalse on the marker row of a career page the platform does not serve
errorstring | nullWhy a row is a marker row; null on job rows
fetchedAtstringWhen the career page was read, ISO 8601 UTC

Rows of mode: "departments" and mode: "locations" carry account, companyName, facetType, name, locationCode (locations only), openJobs, filterUrl (the source's own filtered career-page link), url, found, error, fetchedAt:

{
"account": "remote-recruitment",
"companyName": "Remote Recruitment",
"facetType": "location",
"name": "South Africa",
"locationCode": "ZA",
"openJobs": 84,
"filterUrl": "https://apply.workable.com/api/v1/widget/accounts/694803?location=ZA",
"url": "https://apply.workable.com/remote-recruitment/",
"found": true,
"error": null,
"fetchedAt": "2026-09-26T20:47:53.626Z"
}

Dataset views: Jobs (company, title, department, city, country, remote, employment type, publish date, days online, job page), Job details (function, industry, seniority, education, location text, internal code, apply link), Departments & locations (the dictionary modes). descriptionHtml and descriptionText are in no view on purpose — they would break a table; read them through the API or with fields.

Every run also writes a SUMMARY record to the default key-value store: per career page how many postings were on the page, how many matched, how many were written, which filter values matched nothing, and the run's status sentence.

Use it from code / agents

curl -X POST "https://api.apify.com/v2/acts/yadroo~workable-jobs/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"accounts":["devsinc-17","prox-works"],"titleKeywords":["engineer"],"maxItems":20,"fields":["companyName","title","city","countryCode","publishedOn","url"]}'
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('yadroo/workable-jobs').call({ accounts: ['wantable-careers'], includeDescription: true, maxItems: 5 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("yadroo/workable-jobs").call(run_input={"accounts": ["devsinc-17"], "postedWithinDays": 7, "maxItems": 25})
items = client.dataset(run["defaultDatasetId"]).list_items().items

MCP: add https://mcp.apify.com to Claude / Cursor / any MCP client and call the yadroo/workable-jobs tool with the same JSON input.

Agent and pipeline tips: use fields to keep rows narrow and prompts short; schedule the actor daily with postedWithinDays: 7 plus onlyNew: true so each run writes only postings you have not seen (the memory lives in the actor's own key-value store workable-jobs-state, keyed by career page); run mode: "departments" once per employer to learn the department names before you filter on them.

Pricing

Pay per event: $0.001 per run start + $0.0015 per dataset row. Every run is charged the start event, also a run that ends with a single found: false row. Marker rows count as rows and take their slot inside maxItems, so a run never charges for more rows than you asked for. Apify plan discounts apply to both events: −10 % on Bronze, −20 % on Silver, −30 % on Gold and above.

RunRowsCost at list price
One career page, 25 newest jobs25$0.001 + 25 × $0.0015 = $0.0385
The default input: three career pages, 50 rows50$0.001 + 50 × $0.0015 = $0.0760
Daily watchlist of 30 employers, 400 new postings400$0.001 + 400 × $0.0015 = $0.6010
Departments of two career pages18$0.001 + 18 × $0.0015 = $0.0280

includeDescription costs nothing extra on the source side — the descriptions arrive in the same request — it only makes the rows about twenty times larger. Compute is negligible: 256 MB and a few seconds per career page, no browser.

Limits & FAQ

  • Coverage is per career page. The actor reads the accounts you name; there is no keyword search across all employers and no company discovery, because the platform's cross-company job board disallows crawling its search paths in robots.txt. Bring your own list of slugs.
  • Only published jobs. Drafts, internal and archived postings are not served publicly, and neither are they here.
  • No salary. This payload carries no pay range or pay-transparency field. educationLevel and experienceLevel are the closest things to a requirement summary.
  • Publish dates have day resolution. publishedOn is a date without a clock time, so the finest monitoring window is postedWithinDays: 1; for a tighter loop schedule the actor with onlyNew: true. createdOn is when the posting was created in the system and can be years older than the publish date.
  • Employer-optional fields are often empty: department, function, industry, employment type, seniority, education, internal code. Filter on them to narrow a large board, and expect the blank ones to disappear from the result.
  • One remote flag, no hybrid. A hybrid role appears as remote or on-site depending on how the employer filled the form.
  • Dictionary counts are the source's own numbers and can differ from the number of job rows a career page returns (duplicate postings and locations an employer hides are counted differently). Treat openJobs as the count the career page shows, not as a row count.
  • Large boards come in one response. No pagination parameter is documented and boards of up to 249 postings came back complete; maxItems cuts the rows, never the request. A career page with thousands of postings is untested — set maxItemsPerAccount when you watch a very large employer.
  • Unknown slug vs. empty career page. A slug the platform does not serve always produces one found: false row with an explanation, and the run still succeeds; a career page that exists but publishes nothing right now is reported in the log, the status message and SUMMARY, and gets a marker row only when the run would otherwise write nothing. A run fails only when the source answered none of its requests.
  • Rate limits and politeness. One request per career page per mode, sequential, with retry and backoff on 429 and 5xx. The endpoints are the ones the vendor documents for building a careers page, robots.txt allows them, and the actor never touches candidate or application endpoints — no personal data is collected. The site's content signals allow search and AI input but not model training; we read the postings, we do not train on them.
  • If the platform ever puts these endpoints behind a wall, this actor will be marked paused in the store rather than trying to work around it.

Made by Yadroo. Sibling actors: greenhouse-jobs (the same job for Greenhouse boards), hh-kz-vacancies (vacancies in Kazakhstan and the CIS), github-repo-intel, domain-intel, sec-company-financials (company research around a hiring signal).