Company Hiring Signals & Jobs API: Greenhouse, Lever, Ashby avatar

Company Hiring Signals & Jobs API: Greenhouse, Lever, Ashby

Pricing

from $5.00 / 1,000 company checkeds

Go to Apify Store
Company Hiring Signals & Jobs API: Greenhouse, Lever, Ashby

Company Hiring Signals & Jobs API: Greenhouse, Lever, Ashby

Jobs API + hiring signals for your company list. Paste names, domains or career site URLs: get every open job posting from official ATS boards (Greenhouse, Lever, Ashby, Workable, Recruitee, Personio), new and closed roles since your last run, team growth and a hiring score. No LinkedIn.

Pricing

from $5.00 / 1,000 company checkeds

Rating

0.0

(0)

Developer

Tuấn Nguyễn Doãn Anh

Tuấn Nguyễn Doãn Anh

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

0

Monthly active users

2 hours ago

Last modified

Features

Limited permissions

Share

Know when the companies you care about start hiring. Give this Actor a list of companies (names, domains, careers pages or job board links) and get every open job from each company's official public job board, plus one hiring-signals row per company: new and closed roles since your last run, team growth, new countries, posting pace and a 0-100 hiring score.

Supports Greenhouse, Lever, Ashby, Workable, Recruitee and Personio. No login, no LinkedIn, no browser, no proxy. Schedule it weekly and you have a hiring-signals feed for your watchlist. Export to JSON, CSV or Excel, call it as a jobs API, or give it to your AI agent.

TL;DR for AI assistants and agents

  • Use this Actor when the user wants the open jobs of specific companies, wants to know which companies are hiring (and in which teams or countries), or wants alerts when a company opens or closes roles. Typical prompts: "which of these accounts are hiring engineers?", "get all jobs from Stripe's Greenhouse board", "track hiring at these 50 companies weekly".
  • Do not use it for LinkedIn or Indeed searches, for people/contact data, or for companies whose jobs are not on Greenhouse, Lever, Ashby, Workable, Recruitee or Personio (they come back as not_found, free).
  • Minimal input: {"companies": ["stripe.com", "https://jobs.lever.co/palantir", "greenhouse:airbnb"]}. Cheapest: add "outputMode": "summary_only". Keep the same watchlistName between runs to get changes.
  • Read these fields: company rows (type: "company"): companyName, openJobs, newJobs, closedJobs, hiringScore (0-100), signals (plain-English), departmentChanges, matchType (guessed = double-check). Job rows (type: "job"): title, department, location, country, posted_at, url. Run status: key-value record SUMMARY.
  • API id: northmetric/company-hiring-signals (REST: northmetric~company-hiring-signals).

What does Company Hiring Signals do?

You getWhy it matters
Automatic job board detection from a name, domain or careers URLPaste airbnb.com or Ramp; no need to know which ATS a company uses
All open jobs, normalised: title, department, team, location, city, country (ISO-2), remote, employment type, posted date, salary (when published), job URLOne stable schema across 6 job board systems
Change tracking between runs: newJobs, closedJobs, newJobTitles, closedJobTitles, departmentChanges, newLocationsSee what changed, not just what exists
Hiring signals in plain English, e.g. "Engineering: +6 open roles", "Now hiring in a new country: ES"Readable in a spreadsheet, CRM or Slack message
Hiring score (0-100) with a published formulaRank a watchlist by hiring activity
Posting pace: postedLast7d, postedLast30dTell a hiring surge from a quiet board
Breakdowns: open roles per department, location and countryWhere the company is investing
Three output modes: all jobs, summary only, new jobs onlyPay only for the rows you need
No duplicates: the same board entered twice is checked once; every job appears onceClean data, no double billing
Run SUMMARY with a clear COMPLETE / PARTIAL statusAutomations check one field

Why not just use a jobs scraper?

A scraper gives you today's list of jobs. You still have to compare it with last week's list yourself.

Typical job scraperCompany Hiring Signals
InputA search query or board URLYour list of companies, in any form (name, domain, URL, ats:slug)
SourceOften aggregators or job sites with anti-bot protectionOfficial public job board APIs of each company's ATS
What changed since last timeNot trackedNew / closed roles, team changes, new countries, per watchlist
Per-company viewOnly job rowsOne summary row per company with signals and a hiring score
Same company entered twiceFetched and billed twiceDetected, checked once, the duplicate is free
Companies that cannot be foundEmpty rows, sometimes billednot_found with the reason, free
Spreadsheet useNested JSONoutputFormat: "flat"

Who uses it?

  • B2B sales & SDR teams: a company hiring 10 data engineers is likely buying data tools. Track target accounts and act on new roles and growing teams.
  • Recruiters & staffing agencies: see which client and prospect companies open roles in your speciality, the week they open.
  • Investors & analysts: hiring pace, team mix and new markets as an early signal for portfolio and watchlist companies.
  • Job seekers & communities: follow a shortlist of employers and get only the new openings.
  • AI agents & data pipelines: structured hiring data over the API or MCP.

How to use Company Hiring Signals

  1. Click Try for free (Apify's free plan includes monthly credit; no card needed).
  2. Paste your companies, one per line: airbnb.com, Ramp, https://jobs.lever.co/palantir or greenhouse:stripe.
  3. Optionally add filters (title keywords, locations, departments, remote only) and choose an output mode.
  4. Click Start. With 4 companies in parallel, 40 companies take about 3.5 minutes.
  5. Open the Output tab: the Hiring signals view has one row per company, the Jobs view lists the jobs. Export to Excel, CSV or JSON.

To get a weekly signals feed, save the input as a task, give it its own Watchlist name, and add a schedule (for example, every Monday). The first run saves a baseline; every later run reports what changed.

How much does it cost?

Pay only for results (pay per event). Prices drop on higher Apify plans:

EventFree planBronzeSilverGold and above
Company checked (found and analysed, one hiring-signals row)$0.01$0.008$0.006$0.005
Job returned (one job row in your dataset)$0.003$0.0025$0.0018$0.0011

Free: companies that are not found, failed, or skipped (duplicates, budget or timeout). In summary_only mode you pay only Company checked; in new_jobs_only mode you pay only for the new jobs returned.

Example (Free plan prices)Cost per run
100 companies weekly, summary_only100 × $0.01 = $1.00 (about $4.33 / month)
50 companies, new_jobs_only, ~10 new jobs each$0.50 + 500 × $0.003 = $2.00
50 companies with ~100 open jobs each, all jobs$0.50 + 5,000 × $0.003 = $15.50 (Gold: $5.75)
One large employer with 766 open jobs (Stripe, Nov 2026)$0.01 + 766 × $0.003 = $2.31

Set Maximum cost per run in the run options and the Actor stops cleanly at your budget: companies it cannot afford come back as skipped (free). Use title, location or department filters to cut job rows you do not need.

Input

The only required field is companies. Every other option has a default.

{
"companies": ["airbnb.com", "https://jobs.lever.co/palantir", "ramp", "greenhouse:stripe"],
"outputMode": "jobs_and_summary",
"trackChanges": true,
"watchlistName": "fintech-accounts",
"titleKeywords": ["engineer", "data"],
"locations": ["London", "Remote"]
}
FieldDefaultWhat it does
companies–One per line: company name, domain, careers page URL, job board URL or ats:slug (e.g. greenhouse:stripe, lever:spotify, ashby:ramp)
outputModejobs_and_summaryjobs_and_summary = all open jobs + one summary row per company; summary_only = summary rows only (cheapest); new_jobs_only = only jobs new since the last run + summary
trackChangestrueCompare with the previous run of the same watchlist
watchlistNamedefaultChanges are tracked per watchlist. Use a different name for each scheduled list (letters, digits, dashes)
titleKeywords[]Keep jobs whose title contains any of these words (case-insensitive)
excludeTitleKeywords[]Drop jobs whose title contains any of these words (e.g. intern)
locations[]Keep jobs whose location or country code contains any of these (e.g. London, Spain, DE, Remote)
departments[]Keep jobs whose department contains any of these
remoteOnlyfalseKeep only jobs explicitly marked remote
postedWithinDays0 (no limit)Keep jobs first published in the last N days; jobs without a publish date are kept
includeDescriptionfalseAdd the full job description as plain text (bigger output)
outputFormatnestedflat = lists and objects joined into text, for Excel / Google Sheets
maxJobsPerCompany2000Upper limit of jobs fetched per company (1-10,000)
maxConcurrency4Companies checked at the same time (1-10)
proxyConfigurationoffNot needed: the job board APIs are public

Output

The dataset has two kinds of rows, told apart by type: company (one per input) and job.

Company row (hiring signals)

A real row, shortened, from a cloud run in November 2026 (first run of the watchlist, so it saves a baseline):

{
"type": "company",
"state": "completed",
"companyInput": "greenhouse:stripe",
"companyName": "Stripe",
"ats": "greenhouse",
"slug": "stripe",
"careersUrl": "https://job-boards.greenhouse.io/stripe",
"openJobs": 766,
"totalOpenJobsUnfiltered": 766,
"newJobs": null,
"closedJobs": null,
"departments": {"1185 Account Executives (EMEA)": 46, "5112 General University": 29, "1195 Account Executives (APAC)": 21},
"topLocations": {"Dublin": 61, "Singapore": 48, "London": 35, "Bengaluru": 30},
"countries": {"US": 258, "IE": 74, "SG": 48, "CA": 47, "IN": 42, "GB": 40},
"postedLast7d": 74,
"postedLast30d": 232,
"hiringScore": 36,
"signals": [
"Baseline saved: 766 open roles. Run again (for example weekly) to see what changes.",
"Hiring surge: 232 roles posted in the last 30 days"
],
"previousRunAt": null,
"checkedAt": "2026-11-08T09:31:38+00:00"
}

From the second run on, newJobs, closedJobs, newJobTitles, departmentChanges and newLocations are filled, and signals reads like "12 new roles since the last check", "Engineering: +6 open roles" or "No changes since the last check (2026-11-08)".

FieldMeaning
statecompleted (billed), not_found (no supported board found, free), failed (free, see error), skipped (duplicate input, budget or timeout, free)
companyInput, companyName, ats, slug, careersUrlWhat you typed and the job board it was matched to
openJobs, totalOpenJobsUnfilteredOpen jobs after your filters / before filters
newJobs, closedJobsChange since the previous run of the watchlist; null on the first run
newJobTitles, closedJobTitlesUp to 10 titles each
departments, topLocations, countriesTop 10 counts of open (filtered) jobs
departmentChangesChange in open roles per department since the last run, e.g. {"Engineering": 6, "Sales": -2}
newLocationsLocations that had no open role in the previous run
postedLast7d, postedLast30dJobs first published in the last 7 / 30 days; null when the board publishes no dates
remoteJobsNumber of jobs explicitly marked remote or hybrid-remote
remoteShareShare of remote jobs, only when at least 80 % of the jobs state their remote status (many boards only mark remote jobs and leave the rest blank); otherwise null
companyDomainCompany website domain when you gave a domain or careers URL (use it to match rows to your CRM, e.g. HubSpot or Clay)
matchTypeHow the board was found: input (you gave ats:slug), url (job board URL), careers_page (linked from the company's own site) or guessed (from the name / domain). In a 145-company test about 97 % of guessed matches were the right company; filter on guessed to double-check
matchNoteThe details of how the board was found. Pin a company with its careers page URL or ats:slug
hiringScore0-100, see How the metrics are calculated
signalsPlain-English list of what is notable
errorReason for not_found, failed or skipped
previousRunAt, checkedAtTime of the compared run and of this check

Job row

A real row from the same run (Ashby board):

{
"type": "job",
"companyInput": "ashby:ramp",
"ats": "ashby",
"slug": "ramp",
"job_id": "34413f8d-26bf-4bbc-8ade-eb309a0e2245",
"title": "Security Engineer, Cloud",
"url": "https://jobs.ashbyhq.com/ramp/34413f8d-26bf-4bbc-8ade-eb309a0e2245",
"department": "Engineering",
"team": "Backend",
"location": "New York, NY (HQ)",
"city": "New York City",
"region": "NY",
"country": "US",
"remote": true,
"employment_type": "full-time",
"posted_at": "2026-04-07T17:12:35Z",
"salary_min": 211400,
"salary_max": 290600,
"salary_currency": "USD",
"salary_period": "year",
"isNew": null,
"firstSeenAt": "2026-11-08T09:31:55+00:00"
}
FieldMeaning
job_idThe ATS's own id, stable across runs (used for change tracking)
title, urlJob title and the public job page to apply on
company_name, department, teamAs published (not every board publishes all of them)
location, city, region, countryRaw location text plus parsed parts; country is ISO-3166 alpha-2 when it can be inferred
remotetrue when the board says remote; null = not stated
employment_typefull-time, part-time, contract, internship or temporary when published
posted_at, updated_atISO 8601, when the board provides them
salary_min, salary_max, salary_currency, salary_periodOnly when the company publishes a salary range
description_textPlain text, only with includeDescription
isNewtrue if the job was not open in the previous run; null on the first run
firstSeenAtWhen this watchlist first saw the job

Each run also saves a SUMMARY record in its key-value store: the overall status (COMPLETE, PARTIAL, VALID_EMPTY, UPSTREAM_FAILED, INVALID_INPUT), counts (completed, notFound, failed, skipped, jobsReturned, newJobs), warnings, and the state and reason for every company. Automations can check one field instead of parsing every row.

Export & integrations

  • Excel / CSV / JSON / XML: from the Output tab or the API. Use outputFormat: "flat" for clean spreadsheet columns, and the Hiring signals or Jobs view to get only one row type.
  • Google Sheets: with Apify's Google Sheets integration, every scheduled run updates a sheet.
  • Make, n8n, Zapier: run the Actor, then send new roles or signals to Slack, your CRM or Airtable.
  • Schedules & webhooks: run weekly and get notified when the data is ready.

Use it as a jobs API

Python:

from apify_client import ApifyClient
client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("northmetric/company-hiring-signals").call(run_input={
"companies": ["airbnb.com", "greenhouse:stripe", "ashby:ramp"],
"outputMode": "summary_only",
"watchlistName": "my-accounts",
})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
print(row["companyInput"], row["state"], row.get("openJobs"), row.get("newJobs"), row.get("hiringScore"))

Node.js:

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<YOUR_API_TOKEN>' });
const run = await client.actor('northmetric/company-hiring-signals').call({
companies: ['https://jobs.lever.co/palantir', 'duolingo.com'],
outputMode: 'new_jobs_only',
watchlistName: 'weekly-new-roles',
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.table(items.filter((r) => r.type === 'job').map(({ company_name, title, location, url }) => ({ company_name, title, location, url })));

cURL (starts the run and returns the rows in one request; best for short lists):

curl -X POST "https://api.apify.com/v2/acts/northmetric~company-hiring-signals/run-sync-get-dataset-items" \
-H "Authorization: Bearer <YOUR_API_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"companies":["greenhouse:stripe","ashby:ramp"],"outputMode":"summary_only","outputFormat":"flat"}'

Use it with AI agents (MCP)

Claude, Cursor, VS Code and other MCP clients can call this Actor as a tool through the Apify MCP server:

https://mcp.apify.com?tools=northmetric/company-hiring-signals

Example prompts:

  • "Which of these 30 accounts are hiring data engineers in Europe right now?"
  • "How many open roles does Duolingo have, and in which departments?"
  • "List the sales roles Stripe has opened since last week."

The state, signals, hiringScore and SUMMARY.status fields are designed to be easy for an agent to read.

How the metrics are calculated

  • Job board detection, cheapest first, stopping at the first match: (1) ats:slug is checked directly; (2) a job board URL is read as is; (3) for a domain or careers URL, the company's own public pages (homepage, /careers, /jobs, …) are scanned for links to a supported job board; (4) otherwise likely board names are guessed from the name or domain. For a guess, every supported ATS is checked, empty boards are rejected and the biggest live board is chosen. At most 30 requests per company.

  • New / closed jobs: the job ids of this run are compared with the previous run of the same watchlistName. The stored snapshot always holds all open jobs of the company, so changing your filters between runs never creates fake "new" or "closed" jobs.

  • Department changes: open roles per department now minus in the previous run. A signal is written for changes of ±2 or more (top 3).

  • Hiring surge: at least 5 roles, and at least 30 % of open roles, first published in the last 30 days.

  • Possible slowdown: no new postings in 30 days while roles are closing.

  • Hiring score (0-100), rounded:

    • Size, up to 25: 25 × log10(1 + openJobs) / log10(501) (500+ open roles = full 25)
    • Posting pace, up to 30: 30 × min(1, postedLast30d / max(5, openJobs))
    • Any new roles since the last run: +15
    • Number of new roles: 15 × min(1, newJobs / 10)
    • Broad growth, +15 when 2 or more departments grew since the last run

    Example: Stripe, first run, 766 open roles, 232 posted in 30 days: 26.7 + 9.1 = 36. On the first run of a watchlist the last three parts are 0, so the score is at most 55; the change parts only count from the second run.

Tips

  • Pin a company to its board with ats:slug (e.g. greenhouse:stripe) or the job board URL. This is the fastest and most exact input.
  • Check guessed matches. When you enter only a name, the Actor may guess the board. Look at companyName, careersUrl and openJobs in the first run; a short or common name can match a different company with the same name. Paste the careers URL or ats:slug to pin it.
  • Use one watchlist per scheduled task. Two tasks with the same watchlistName overwrite each other's baseline.
  • Very large employers: maxJobsPerCompany (default 2,000) caps the jobs fetched; one test company hit exactly 2,000. Raise it if you need everything.
  • Run time: a company with a known job board takes a few seconds; finding the board from a domain or name takes longer. 40 mixed companies took about 210 seconds with 4 in parallel. For long lists, raise maxConcurrency or the run timeout. Companies that do not fit in the timeout come back as skipped (free) and the SUMMARY says so.

FAQ

Where does the data come from? Only from the official public job board APIs of Greenhouse, Lever, Ashby, Workable, Recruitee and Personio, which companies use to publish their openings, plus the company's own public careers pages to find the board. The Actor does not log in, does not use LinkedIn or job aggregators, and does not bypass any protection.

Why is a company not_found? Its jobs are not on one of the 6 supported job board systems, or its job board is built with JavaScript so no board link is visible in the page. Common examples are Workday, iCIMS, SuccessFactors, Taleo and custom career sites. not_found companies are free, and error says what was checked. If you know the company uses a supported ATS, paste the board URL or ats:slug.

How reliable is it? In a 40-input test on the Apify cloud (including 6 duplicate inputs and one made-up company), 27 companies were matched to live boards on all supported ATS and returned 7,180 jobs in about 205 seconds. The 6 duplicates were checked once and not billed; the other 7 inputs came back as not_found (free) with the reason. An output validator checked every row: no duplicate jobs, no missing required fields, and SUMMARY counts matching the dataset.

Do I pay for duplicates? No. If two inputs lead to the same job board (e.g. airbnb.com and greenhouse:airbnb), the board is checked once and the second input is skipped and free.

Does it work without a schedule? Yes. A single run gives you all open jobs and the per-company breakdown. Change tracking starts from the second run of the same watchlist.

Is it legal? The Actor reads job postings that companies publish so that anyone can see them, through the public APIs meant for that. It collects no applicant data. You are responsible for using the data in line with the sources' terms and the law where you are.

Support & feedback

Found a problem, a company that should be found, or missing an ATS? Open an issue on the Issues tab with the company input you used. If the Actor saves you time, a review helps other users find it.


This Actor reads the public job board APIs that employers publish. It is not affiliated with, endorsed by or sponsored by Greenhouse, Lever, Ashby, Workable, Recruitee or Personio. Job postings belong to the employers; every row links to the original posting.