Workable Jobs Scraper: Pull Every Posting From Any Workable Careers Board
Workable is one of the most common ATS choices for startups and mid-size companies — if you've ever seen a careers page at apply.workable.com/{subdomain} or {subdomain}.workable.com, that's it. Thousands of employers run on it, and every one of those boards is served by the same public widget API. This post covers what that data looks like, why hand-rolling a scraper against it gets annoying past one company, and how the Workable Jobs Scraper handles it for you.
Why Workable job data is worth pulling
Workable exposes open postings — title, department, location, remote flag, and description — through a public widget endpoint that any careers page already calls client-side. No login, no API key. That makes it a solid signal for:
- Recruiters and sourcers pulling every open req from a target employer's board in one pass
- Job-board aggregator operators who want Workable coverage alongside Workday, SmartRecruiters, Greenhouse, Lever, and Ashby
- SDR / BD teams using active hiring as a buying-intent signal — a company scaling its engineering team is a company that might need your product
- HR-tech pipeline builders wiring structured rows into a CRM, dashboard, or n8n/Make workflow on a schedule
The naive approach, and where it gets annoying
apply.workable.com/api/v1/widget/accounts/{subdomain} returns a full board in one call, which sounds like the easy path — and for one company, it basically is. The friction shows up in the details: Workable accepts both {subdomain}.workable.com and apply.workable.com/{subdomain} as valid public URL forms and you have to normalize both to the same subdomain before you can hit the widget endpoint. The server-side facet filters that look like they should narrow results (department, location) don't reliably work against this endpoint, so filtering has to happen after the fetch, not before. And subdomains that don't exist need to fail gracefully rather than take down a whole multi-company batch. Multiply that across dozens of subdomains and it's a script you're now maintaining, not writing once.
What we built instead
The Workable Jobs Scraper hits the same public widget API and absorbs the annoying parts: curl-cffi browser-fingerprint impersonation on every request, retries with backoff on transient 429/5xx, and normalization across whichever mix of subdomains you feed it. It also documents the client-side-filter constraint honestly instead of pretending server-side filters work — searchQuery, department, and location all run after the single fetch per company, at no extra request cost, so you're never billed twice for the same board.
Run it via the Apify Python SDK:
from apify_client import ApifyClient
client = ApifyClient("APIFY_TOKEN")
run = client.actor("DevilScrapes/workable-jobs-scraper").call(run_input={
"companies": ["remotebase"],
"maxResultsPerCompany": 25,
"includeDescription": True,
"proxyConfiguration": {"useApifyProxy": True},
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(item["title"], item["location_country"], item["is_remote"])
Or paste the same JSON into the Store page input form directly.
Sample output row:
{
"job_id": "6E7795E82F",
"title": "AI Engineer",
"company": "remotebase",
"company_name": "Remotebase",
"department": "Core",
"location_city": null,
"location_country": "Pakistan",
"is_remote": true,
"url": "https://apply.workable.com/j/6E7795E82F",
"posted_date": "2026-04-01",
"description_html": null,
"scraped_at": "2026-07-23T15:00:00Z"
}
The technically interesting bit
Workable accepts a bare subdomain, apply.workable.com/{subdomain}, or {subdomain}.workable.com as equally valid public entry points, but they don't all resolve identically against the widget API — you have to extract the actual account subdomain regardless of which form someone pastes in. And the facet filters that look like a free server-side narrowing option (department=, location=) are a trap: they don't reliably filter this specific endpoint's results, so a scraper that trusts them silently returns incomplete data. We treat that as a documented constraint, not a bug to route around with retries — filtering happens client-side, after the one fetch, so you always get the full board first.
Pricing — no fine print
Pay-Per-Event: $0.005 per run plus $0.0015 per job posting written to the dataset — $1.50 / 1,000 results. No data lands, no charge beyond the warm-up fee. New Apify accounts get $5 of free credit, no card required.
Honest limitations
A posting listed at multiple locations emits only its first location as one row. Only currently-live postings are returned — there's no historical archive of removed postings. And as noted above, filtering runs client-side after the fetch, since Workable's own facet params don't reliably narrow this endpoint.
Try it, and the rest of the fleet
Workable is one piece of the ATS puzzle. We also run the Workday Jobs Scraper, the SmartRecruiters Jobs Scraper, and the Multi-ATS Jobs Scraper covering Greenhouse, Lever, and Ashby in one normalized schema — same keyless, pay-per-result model throughout.
Grab the Workable Jobs Scraper on the Apify Store and try it with the free $5 credit. Hit a board that behaves differently, or need a field we don't expose? Reach out via DevilScrapes on Apify — we ship fixes fast. Browse the rest of the fleet there too.
Which Workable board would you pull first — and what would you do with the "actively hiring" signal?
Top comments (0)