Company Domain to Job Postings: Greenhouse, Lever, Ashby avatar

Company Domain to Job Postings: Greenhouse, Lever, Ashby

Pricing

Pay per event

Go to Apify Store
Company Domain to Job Postings: Greenhouse, Lever, Ashby

Company Domain to Job Postings: Greenhouse, Lever, Ashby

Paste company domains, get their live job postings. Finds each company's ATS board across Greenhouse, Lever, Ashby, Recruitee, SmartRecruiters and Personio, verifies it really belongs to them, and returns every open role in one schema with salary, seniority and remote type. No slug hunting.

Pricing

Pay per event

Rating

0.0

(0)

Developer

youssef farhan

youssef farhan

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Share

Paste company domains, get their live job postings. Finds each company's ATS board across Greenhouse, Lever, Ashby, Recruitee, SmartRecruiters and Personio, verifies it really belongs to them, and returns every open role in one schema with salary, seniority and remote type. No slug hunting.

Every other multi-ATS job scraper makes you supply a board slug first: greenhouse:stripe, lever:spotify. Your CRM export does not contain those. It contains stripe.com.

This Actor closes that gap. Give it a domain and it finds the company's careers page, works out which applicant tracking system they use, verifies the board actually belongs to that company, and returns every open role in one unified schema.

stripe.com -> greenhouse:stripe -> 578 jobs
channable.com -> recruitee:channable -> 13 jobs
personio.com -> personio:personio -> 1 job
monzo.com -> greenhouse:monzo -> 110 jobs
example.com -> no board found -> reported, not guessed

No login. No API key. No proxy. No browser.


Why verification matters

Guessing a board slug from a domain is easy and wrong often enough to poison a dataset. During development, a plain guess resolved:

  • personio.com to an unrelated Recruitee demo board named "FD Sandbox"
  • example.com, IANA's reserved domain, to a Greenhouse board belonging to "Democorp"
  • neon.tech to a different company also called Neon, on Lever

All three looked like clean successes. All three would have been billed and shipped into your dataset.

So this Actor accepts a board only when it can prove ownership, by either of two independent routes:

  1. The board mentions the company's own domain, in apply URLs, company fields or description text. Measured across the known cases, correct boards mentioned the domain between 32 and 840 times, and wrong boards mentioned it zero times.
  2. The board's company name contains the domain label. This catches boards with no description text at all, where route 1 has nothing to read.

A board the company links to from its own careers page is accepted as a second pass even without either signal, and flagged in resolved_via so you can tell the difference.

A domain that cannot be resolved is reported, never guessed. Unresolved domains appear in the COMPANY_REPORT record with their status, and they are not charged.


Supported ATS platforms

ATSSourceDescriptionsStructured salary
Greenhouseboards-api.greenhouse.ioYesParsed from description
Leverapi.lever.coYesParsed from description
Ashbyapi.ashbyhq.comYesYes, native compensation tiers
Recruitee{company}.recruitee.comYesYes, native salary object
SmartRecruitersapi.smartrecruiters.comYes, one extra request per jobParsed from description
Personio{company}.jobs.personio.deYesParsed from description

Adding a seventh platform means adding one file. If one you need is missing, suggest it here.


Quick start

Example input

{
"companies": [
"stripe.com",
"monzo.com",
"channable.com",
"https://www.airtable.com"
],
"keywords": ["engineer", "data"],
"remoteOnly": true,
"includeDescription": true,
"descriptionFormat": "markdown",
"incremental": false,
"maxItems": 0
}

You can mix in boards you already know, and they skip the lookup entirely:

FormatExampleBehaviour
Company domainstripe.comResolved and verified
Company website URLhttps://www.airtable.comResolved and verified
Board slugstripeProbed across all six platforms
Qualified sluglever:spotifyUsed directly, no lookup
ATS board URLhttps://jobs.ashbyhq.com/rampUsed directly, no lookup

How resolution works

Candidates are collected cheaply, then verified against the real board APIs. The resolved_via field on every row records which route won.

resolved_viaMeaning
homepageAn ATS link was in the site's own homepage markup
careers-pageFound on a careers page the homepage linked to
common-pathFound at /careers or /jobs
careers-subdomainImplied by a careers subdomain the company publishes, e.g. jobs.channable.com
slug-guessGuessed from the domain label, then corroborated
slug-probeYou supplied a bare slug and it was probed across platforms
givenYou named the board directly
...-redirectFound in a redirect target rather than page body
...-unconfirmedThe company linked to this board, but the board never mentions the company

Unified output schema

Every record carries every field, on every platform. A field the source does not publish comes back null rather than missing, so downstream code never branches on which ATS a row came from.

FieldTypeDescription
job_idstringStable id, ats:slug:id. Unique across platforms, steady between runs
atsstringgreenhouse, lever, ashby, recruitee, smartrecruiters or personio
companystringCompany name as published
company_slugstringThe board slug it was scraped from
source_domainstringThe domain this row was resolved from, for joining back onto your input list
titlestringJob title as posted
departmentstringDepartment or job family
teamstringSub-team or job function
locationstringPrimary location string
locationsarrayEvery location on the posting
countrystringFrom the platform field, or inferred from the location text
is_remotebooleanTrue for remote roles. Hybrid is false
remote_typestringremote, hybrid or onsite
employment_typestringfull_time, part_time, contract, temporary, internship, volunteer
senioritystringinternship, entry, mid, senior, staff, principal, lead, manager, director, executive
salary_minnumberLower bound of the pay range
salary_maxnumberUpper bound of the pay range
salary_currencystringISO 4217 code, e.g. USD, EUR, GBP
salary_periodstringhour, day, week, month or year
salary_rawstringThe pay string as published, so a parsed range can be checked
description_htmlstringFull body as HTML
description_markdownstringFull body converted to Markdown
description_textstringFull body as plain text
apply_urlstringDirect link to the application form
job_urlstringPublic link to the posting
posted_atstringISO 8601 first publication
updated_atstringISO 8601 last edit reported by the platform
scraped_atstringISO 8601 timestamp of this run
change_statusstringnew or updated in incremental mode
resolved_viastringHow the board was found, per the table above

Only the description field you asked for is filled. The other two stay null, so a record never carries the same body three times.

Example output

{
"job_id": "recruitee:channable:2697907",
"ats": "recruitee",
"company": "Channable",
"company_slug": "channable",
"source_domain": "channable.com",
"title": "Technical Customer Support DACH - German speaking",
"department": "Support",
"team": "customer_service",
"location": "Utrecht, Utrecht, Netherlands",
"locations": ["Utrecht, Utrecht, Netherlands"],
"country": "Netherlands",
"is_remote": false,
"remote_type": "hybrid",
"employment_type": "full_time",
"seniority": "entry",
"salary_min": 2850.0,
"salary_max": 2950.0,
"salary_currency": "EUR",
"salary_period": "month",
"salary_raw": null,
"description_html": null,
"description_markdown": "Are you fluent in German and passionate about solving complex problems...",
"description_text": null,
"apply_url": "https://jobs.channable.com/o/technical-customer-support-dach-german-speaking-2/c/new",
"job_url": "https://jobs.channable.com/o/technical-customer-support-dach-german-speaking-2",
"posted_at": "2026-08-03T15:40:43+00:00",
"updated_at": "2026-08-04T07:14:55+00:00",
"scraped_at": "2026-08-16T14:06:44.989721+00:00",
"change_status": null,
"resolved_via": "slug-guess"
}

The company report

Alongside the dataset, every run writes a COMPANY_REPORT record with one row per input, so you always know what happened to each domain:

[
{"input": "channable.com", "domain": "channable.com", "ats": "recruitee",
"slug": "channable", "resolvedVia": "slug-guess", "jobsFound": 13,
"jobsKept": 13, "status": "resolved"},
{"input": "example.com", "domain": "example.com", "ats": null, "slug": null,
"resolvedVia": null, "jobsFound": 0, "jobsKept": 0, "status": "unresolved"}
]

Use cases

Enrich a CRM or prospect list. Your sheet has domains. Feed them in, get back every open role per company, joined on source_domain. No manual slug hunting, no dead ends left unexplained.

Score hiring intent for sales. A company that just opened five RevOps roles has budget. Filter by keywords, run on a schedule, and watch change_status: new.

Track a VC portfolio or competitor set. You know the domains, not the ATS vendors. Resolution handles the difference, and re-checks it on every run in case a company migrates platform.

Build a niche job board. Stable job_id makes upserts trivial. Salary and remote type are already normalized across all six platforms.

Run compensation benchmarking. salary_min, salary_max, salary_currency and salary_period are normalized everywhere, so you can aggregate pay bands by title, seniority and country with no cleaning step.

Feed an AI recruiting agent. Markdown descriptions and a flat schema drop straight into a RAG pipeline with no HTML stripping.


Filters and modes

InputWhat it does
keywordsKeep only titles containing one of these words. Case-insensitive
locationFilterKeep only jobs whose location mentions one of these
remoteOnlyKeep only roles classified as remote. Hybrid does not count
includeDescriptionTurn off to skip description fetching. Makes SmartRecruiters boards dramatically faster
descriptionFormatmarkdown, html or text
incrementalEmit only new or changed jobs since the last run
maxItemsStop after N jobs. 0 means no limit
presetAdd a curated company list: yc_companies, ai_labs or unicorns

How incremental mode works

Each job is fingerprinted over the fields that matter (title, location, department, employment type, salary, apply URL, updated date and description). Fingerprints live in a named key-value store that survives between runs.

  • First run: everything is new, because nothing has been seen yet.
  • Later runs: only jobs whose fingerprint changed come back, tagged updated, plus genuinely new postings tagged new.

scraped_at is excluded from the fingerprint, since it changes on every run by definition.


Frequently asked questions

What if a company does not use one of the six platforms? It is reported as unresolved in COMPANY_REPORT and costs you nothing. It is never guessed at and never silently dropped.

How accurate is the resolution? Across a 13-domain test set spanning all six platforms, 10 resolved and every one was correct. The three that did not resolve genuinely had no supported board: IANA's reserved example.com, a company acquired and folded into another's careers site, and one using an unsupported ATS. Zero false positives is the design goal, and it is why boards must prove ownership before they are accepted.

Why did my domain not resolve when the company clearly has a Greenhouse board? The most common cause is a fully JavaScript-rendered careers page that never puts the board URL in the HTML. This Actor is HTTP-only by design, which is what makes it fast and cheap. If you know the slug, pass greenhouse:theirslug directly and it will be used with no lookup.

Do I need an API key for any of these platforms? No. All six endpoints are the public job-board APIs those platforms serve to render company career pages.

Does this need a proxy? No. These are public APIs with no anti-bot layer, so there is no proxy configuration at all.

What happens if an input is wrong? It is logged and skipped, and the run continues. A typo can never end a run or empty a dataset.

Why is salary_min empty on some jobs? Most companies do not publish pay. Ashby and Recruitee expose structured compensation, read directly. For the other four the Actor parses ranges out of the description, accepting a match only when it sits next to a pay-related word and falls in a plausible band, so unrelated figures in the prose are not misread.

Why is country sometimes empty? Because guessing wrong is worse than leaving it blank. Two-letter codes that are both an ISO country and a US state (CA, DE, IL, IN, AR, CO, ID) are never resolved from the code alone. Chicago, IL resolves through the city; Boise, ID stays empty.

Can I run this on a schedule? Yes. Pair a schedule with incremental: true and each run returns only that period's movement.


Pricing

$1.00 per 1,000 jobs. $6.00 per 1,000 companies discovered, once each.

Pay per event, and platform usage is free: no compute units on top, no monthly fee beyond your Apify plan.

EventWhat triggers itFreeStarterScaleBusiness
New company board discoveredA domain is matched to a verified board$6.00 / 1,000$6.00 / 1,000$5.50 / 1,000$5.00 / 1,000
Company board readA board is read from a slug you gave or already discovered$2.00 / 1,000$2.00 / 1,000$1.90 / 1,000$1.80 / 1,000
Job postingA job is written to the dataset$1.00 / 1,000$1.00 / 1,000$0.95 / 1,000$0.90 / 1,000
Actor startA run starts$0.00005$0.00005$0.00005$0.00005

Higher plan tiers carry better rates: Starter gets the Bronze discount, Scale the Silver, Business the Gold. Starting a run is effectively free at $0.00005, so scheduling this every hour costs nothing in fixed fees.

Three things make it cheaper than it looks:

  • Discovery is charged once per company, not once per run. Boards that are found get remembered between runs, so the second run over the same domain list bills the $2.00 / 1,000 read rate instead of the $6.00 / 1,000 discovery rate. A remembered board is still fetched live every run, so a company that switches ATS is re-discovered automatically.
  • The two company events never both fire. Discovery already covers reading the board it found.
  • Unresolved domains cost nothing. Discovery bills only on success, after the board has been verified against the live API.
ScenarioCost
Trial: 3 new domains, 150 jobs$0.17
CRM enrichment, first run: 50 domains (40 resolve), 4,000 jobs$4.24
Same list, every run after: 40 remembered boards, 4,000 jobs$4.08
Known slugs, no discovery: 20 boards, 2,000 jobs$2.04
Hourly incremental watch: 40 remembered boards, 40 changed jobs$0.12 per run, about $86 a month

Free-plan runs are limited to 3 companies and 50 jobs. Subscribe to an Apify plan to remove the caps.


Notes and limits

  • Resolution is HTTP-only. A careers page that renders its board purely in JavaScript will not resolve; pass the slug directly in that case.
  • SmartRecruiters answers an unknown company with an empty list rather than a 404, so a bare slug is never auto-detected as SmartRecruiters. Use smartrecruiters:Slug for those boards. The slug is case-sensitive there.
  • Personio boards are served in the language the company publishes. The Actor does not force a language, because requesting one the board does not publish returns postings with empty descriptions.
  • Runs are migration-safe. Progress is checkpointed after every company, so a run moved to another host resumes instead of restarting.

๐Ÿ’ผ Need a custom solution? Reach out at youssefarhan24@gmail.com ๐ŸŒ Got a site in mind? Suggest it here