You do not need LinkedIn Sales Navigator to know which companies are hiring. Almost every company that recruits publishes its open roles on a public applicant tracking system board: Greenhouse, Lever, Ashby, Recruitee, SmartRecruiters or Personio. Those boards are public HTML, they are updated the day a role opens, and they are tied to a company domain. If you have a list of domains, you can turn it into a live, dated list of every open role at those companies in about twenty minutes and for a few dollars.

This guide covers the whole loop: how the domain to board match works, the exact fields you get back, what a run costs on real numbers, the one failure mode that quietly ruins the output, and how to score the results so a sales or recruiting team gets a short list instead of a spreadsheet nobody opens.

How do you find out which companies are hiring right now?

Feed a list of company domains into an actor that resolves each domain to its applicant tracking system board and returns every open role on it. That is exactly what Company Domain to Job Postings does. You paste domains, one per line, and it discovers the matching board across Greenhouse, Lever, Ashby, Recruitee, SmartRecruiters and Personio, verifies the board really belongs to that domain, then returns the open roles in one flat schema.

The important word there is verifies. The naive version of this job is to guess a board slug from the company name, so acme.com becomes boards.greenhouse.io/acme. That guess is wrong often enough to poison a list, because slugs are first come first served and short names are already taken by unrelated companies. The actor checks that the board it found actually maps back to the input domain before it returns a single role, so you are not shipping a prospect list where a quarter of the rows belong to the wrong Acme.

Three practical notes before you start:

  • You do not need to know each company's board URL. Domains in, roles out. That is the entire input.
  • Roles are current. A board reflects what is open today, not a scrape of a job aggregator that lags by weeks.
  • It runs in the Apify cloud, so there are no proxies, no browser automation and no servers on your side.

Why is hiring data a better buying signal than firmographics?

Because hiring tells you what a company is about to spend money on, and firmographics only tell you what it already is. Employee count, industry and revenue band are static for months. A job posting is a dated, budgeted, publicly announced commitment. A company that posts two data engineer roles and a head of analytics in the same month has a budget line for data infrastructure right now, and the person who signs that budget is findable.

The useful patterns fall out of the data itself:

  • Function-specific hiring. Three sales development roles means the outbound team is scaling, which is the moment sales tooling gets bought.
  • First hire in a function. The first ever security engineer, the first RevOps hire, the first localisation manager. First hires almost always come with a tooling budget because nothing exists yet.
  • Sudden geography. A company that only posted in Berlin and now posts three roles in Austin is opening a market, and that comes with new vendors.
  • Technology named in the posting. Job descriptions leak the stack. If you sell an add-on for a platform, roles that require that platform are pre-qualified accounts.

None of that requires a paid intent data provider. It falls out of a public board.

What data do you actually get back?

One row per open role, with the fields a scoring model actually needs:

  • Input company domain
  • Detected ATS provider
  • Verified board URL
  • Job title
  • Department and team
  • Seniority
  • Remote type: remote, hybrid or on-site
  • Location
  • Salary, where the employer publishes it
  • Posted date
  • Apply URL

Two of those do more work than the rest. Posted date is what separates a live signal from a role that has sat open for eight months because nobody is really recruiting for it. Department and team is what lets you count roles per function instead of eyeballing titles, and title text alone is unreliable because every company invents its own ladder.

Export is JSON, CSV or Excel, or you pull the dataset over the Apify API. It works fine as a step in n8n, Make or Zapier if the rest of your stack lives there.

How much does it cost to check 1,000 domains?

Roughly twenty to thirty dollars, and the variable is how many roles each company has open, not how many domains you submit. Pricing is per result at $0.006, and a result is a job posting.

Real arithmetic on a mid-market B2B list:

  • 1,000 domains submitted.
  • Around 35 to 45 percent resolve to a detectable public board. Small companies often use a careers page with no ATS, and very large enterprises use Workday or Taleo instances that are not in this set.
  • Call it 400 companies with boards, averaging 12 open roles each, so about 4,800 results.
  • 4,800 x $0.006 = $28.80.

New Apify accounts get $5 of free credit, which is enough to run 800 results and see whether your domain list resolves well before you spend anything. If you want a hard ceiling, set a max results limit on the run. Set it deliberately though, because a limit truncates the dataset globally rather than per company, so a few role-heavy companies at the top of the list can eat the whole quota. The safer pattern is to split a big list into batches of 200 domains and run them separately.

What goes wrong in practice, and how do you handle it?

The failure that costs you the most is not a blocked request. It is parent company boards. A holding company or a group runs one ATS board for every brand it owns, so eight of your domains all resolve to the same board and you get the same 90 roles duplicated eight times. Your counts are then meaningless, and a per-account score built on them ranks the group's brands as the eight hottest accounts on the list.

The fix is mechanical. Deduplicate on the apply URL, not on the domain, then look for the same verified board URL appearing under more than one input domain and collapse those into a single account before scoring:

from collections import defaultdict

boards = defaultdict(set)
for row in rows:
    boards[row["board_url"]].add(row["domain"])

shared = {b: d for b, d in boards.items() if len(d) > 1}
for board, domains in shared.items():
    print("shared board", board, "->", sorted(domains))

Two smaller issues worth knowing. Stale roles: filter on posted date and drop anything older than about 60 days unless you specifically want long-open roles. And salary coverage: it is only present where the employer published it, which in practice means most postings in Colorado, New York, California and much of the EU, and far fewer elsewhere. Do not build a scoring rule that requires salary to exist.

How do you turn the raw rows into a ranked account list?

Pull the dataset, group by company, and score on recency and function fit rather than raw role count. Raw count just ranks big companies first, which you already knew about.

import requests
from collections import defaultdict
from datetime import datetime, timedelta

DATASET = "YOUR_DATASET_ID"
TOKEN = "YOUR_APIFY_TOKEN"

rows = requests.get(
    f"https://api.apify.com/v2/datasets/{DATASET}/items",
    params={"token": TOKEN, "format": "json", "clean": "true"},
    timeout=120,
).json()

TARGET = {"data", "analytics", "engineering", "revenue operations"}
cutoff = datetime.utcnow() - timedelta(days=45)

scores = defaultdict(int)
seen = set()

for r in rows:
    apply_url = r.get("apply_url")
    if not apply_url or apply_url in seen:
        continue
    seen.add(apply_url)

    posted = r.get("posted_date")
    if posted:
        try:
            if datetime.fromisoformat(posted[:10]) < cutoff:
                continue
        except ValueError:
            pass

    dept = (r.get("department") or "").lower()
    points = 3 if any(t in dept for t in TARGET) else 1
    if (r.get("seniority") or "").lower() in {"head", "director", "vp", "lead"}:
        points += 2

    scores[r["domain"]] += points

for domain, score in sorted(scores.items(), key=lambda kv: -kv[1])[:25]:
    print(f"{score:4d}  {domain}")

That gives a sales team 25 accounts with a defensible reason attached to each one, which is a very different conversation from handing over a CSV of 4,800 job postings.

What about hiring data outside the ATS world?

Two gaps are worth naming, because ATS boards do not cover everything.

The Gulf market runs on job boards, not ATS boards. If your market is the UAE, Saudi Arabia, Qatar, Kuwait, Bahrain or Oman, the demand shows up on Naukrigulf long before it shows up on a Greenhouse board, and a lot of employers there never publish a board at all. The Naukrigulf Scraper covers those six countries at $0.0012 per result and returns what the listing page hides: salary where disclosed, nationality and gender requirements, experience and education required, and whether the poster is a recruitment agency or the direct employer. That agency flag matters more than anything else in that market, because agency reposts inflate every count you try to make.

Technical roles have a second, better source. If you are recruiting engineers rather than selling to their employer, the posting tells you a company wants someone and nothing about who is available. GitHub Developer Leads works the other direction, finding developers by language, location or topic and enriching them with public email, bio, company, top languages, followers and repos at $0.005 per result. Run the hiring list to find which companies are staffing up, then run the developer list to find who could fill those roles.

Both live in the lead generation actors hub alongside the rest of the sourcing tools.

How do you build the list, step by step?

  1. Assemble your domain list. Anything works: a CRM export, a conference exhibitor list, a funding announcement roundup, an existing customer lookalike list. One domain per line, no https:// needed.
  2. Split it into batches of about 200 so a per-run result limit cannot be swallowed by a handful of large employers.
  3. Run Company Domain to Job Postings on the first batch and check the resolve rate. Below roughly 25 percent usually means your list skews to very small companies or to enterprises on Workday.
  4. Export the dataset, collapse shared boards, and drop postings older than 60 days.
  5. Score by function fit and seniority as above, then send the top accounts to whoever is doing the outreach.
  6. Schedule the run weekly. The signal is the change, so a new role appearing is worth more than the standing list you already saw last week.

If the board or job source you need is not covered, that is a request rather than a dead end. Tell me the site at suggest a scraper, which is free, or browse everything already published at the full actor catalogue.

Frequently asked questions

Which applicant tracking systems are supported?

Greenhouse, Lever, Ashby, Recruitee, SmartRecruiters and Personio. Workday and Taleo instances are not covered, which is the main reason large enterprises will not resolve to a board.

Is scraping public job postings legal?

Company career boards are public pages published so that people will read them, and the data returned here is job listing information, not personal data about candidates. Follow the usual rules: respect the site's terms, do not hammer it, and if you contact people afterwards make sure your outreach complies with GDPR, CAN-SPAM or whatever applies in your market.

How fresh are the results?

They are read from the company's own board at run time, so they reflect what is open at that moment. Each row carries a posted date so you can filter for roles opened recently rather than roles that have sat open for months.

What does a run cost?

$0.006 per job posting returned. A thousand domains typically produces four to five thousand postings, so about $25 to $30. New Apify accounts get $5 in free credit, which is enough to test a batch before committing.

Can I run this on a schedule?

Yes. Apify schedules runs hourly, daily or weekly and can push results to your app through a webhook, so a weekly run that diffs against last week's dataset gives you new roles only.

Need help implementing this?

I build custom automation, scraping pipelines, and AI solutions for businesses. 155+ projects delivered with a perfect 5.0 rating. Tell me about your project - I reply within 24 hours.

Start Your Project →