DEV Community

Daniel Meshulam
Daniel Meshulam

Posted on

Search every Greenhouse, Ashby and Workable job board at once, without a company list

Every guide to scraping job boards starts the same way: "first, get the company's board token." Greenhouse, Ashby and Workable each expose a public JSON API per company, so if you know the token, one HTTP call returns every open role. Clean, documented, no login.

The problem is the token. There is no directory of them. If you want "senior backend roles in Berlin" across the whole ecosystem, you would need the board token of every company that might be hiring, which is exactly the list you were hoping the data would give you. The per-company APIs answer "what is Stripe hiring?" but not "who is hiring?"

I run a nightly index that solves the inversion: it already knows 7,131 boards across Greenhouse, Ashby and Workable, and re-reads them every night. 230,508 open roles at the moment I write this. You query it like a search engine instead of crawling anything yourself.

One call, no company list

curl -s "https://api.apify.com/v2/acts/glitchbound~ats-jobs-search/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "title": ["backend engineer*", "platform engineer*"],
    "countries": ["Germany"],
    "workArrangements": ["remote"],
    "postedAfter": "2026-07-01",
    "maxResults": 100
  }'
Enter fullscreen mode Exit fullscreen mode

You get one JSON row per job: title, company, location, the real apply URL on the employer's own site, department, posting date, and salary where the employer published one. A trailing * does prefix matching, a multi-word line is a phrase, and countries is resolved rather than text-matched, so German roles arrive whether the board wrote Berlin, Germany, Munich, DE or DE - Berlin.

The same thing in Python, with the Apify client:

from apify_client import ApifyClient

client = ApifyClient(token="...")
run = client.actor("glitchbound/ats-jobs-search").call(run_input={
    "title": ["data engineer*"],
    "countries": ["Netherlands", "Germany"],
    "seniority": ["senior", "staff"],
    "maxResults": 200,
})
for job in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(job["company"], "|", job["title"], "|", job["url"])
Enter fullscreen mode Exit fullscreen mode

Why this is not "just scrape LinkedIn"

LinkedIn scrapers need an account, and accounts get banned. This index reads the same employer-published APIs that job aggregators are invited to read: no login, no cookies, no proxy, and the apply link goes to the employer's own careers page rather than through an aggregator.

The trade-off is coverage. It holds companies that run their hiring on Greenhouse, Ashby or Workable, which is most of tech, and it does not hold the long tail of companies that only post to LinkedIn. Lever is missing too: their robots.txt blocks the crawler the index is seeded from, and a partial Lever list would be worse than none.

The part I have not seen elsewhere: companies as the row

Flip mode to companies and the same index answers a different question: who is hiring hardest right now?

{
  "mode": "companies",
  "hiringLabels": ["surging", "growing"],
  "enteringNewDepartments": true
}
Enter fullscreen mode Exit fullscreen mode

Each row is a company with its open-role count, how many roles it opened in the last 30 and 7 days, and which departments are new this month. A company opening its first sales or security roles has just changed strategy. Because the index sees every board every night, it can also tell you when jobs close, which a live scraper cannot: you cannot see a posting disappear if you were not there when it existed.

Honest limits

  • 42,894 of the roles are remote; the rest are not, and unlabelled jobs are left unlabelled rather than guessed into a bucket.
  • Salary appears on about 8% of jobs, from Ashby only, because Greenhouse and Workable publish no salary field at all.
  • The index refreshes nightly, not per-request. Follow the url field to the employer's page, which is always authoritative.

Pricing is per job returned, about a dollar per thousand, and a search that matches nothing costs nothing.

The Actor is here: https://apify.com/glitchbound/ats-jobs-search

Questions and edge cases welcome in the comments; if a filter you need is missing, say so and I will likely ship it.

Top comments (0)