Tech Stack Hiring Demand - Greenhouse, Lever & Ashby Jobs avatar

Tech Stack Hiring Demand - Greenhouse, Lever & Ashby Jobs

Pricing

from $10.00 / 1,000 technology index rows

Go to Apify Store
Tech Stack Hiring Demand - Greenhouse, Lever & Ashby Jobs

Tech Stack Hiring Demand - Greenhouse, Lever & Ashby Jobs

Reads open postings on the public Greenhouse, Ashby and Lever job boards you name, detects which of 395 technologies each requires, and returns one row per technology: companies and roles requiring it, share of postings, change since last run, plus adoption events.

Pricing

from $10.00 / 1,000 technology index rows

Rating

0.0

(0)

Developer

Mikkel Bech-Hansen

Mikkel Bech-Hansen

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

20 days ago

Last modified

Categories

Share

Give this actor a list of companies whose job boards run on Greenhouse, Ashby or Lever. It reads every open posting on those boards, runs a curated vocabulary of 395 technologies over the description text, and returns one row per technology rather than one row per job: how many of your companies require it, how many open roles mention it, what share of all postings that is, and how those numbers moved since your last run. It also writes typed events when a specific company starts requiring a technology, stops requiring it, or when a technology's mention share breaks out of its own trailing range.

It is not a job scraper. If you want the postings themselves, several actors on the Store already read these same endpoints. This one throws the postings away and keeps the aggregate, which is the thing you can put on a dashboard and watch week over week.

What you need to know first

The index is a time series, so it needs more than one run to be interesting. The first run against a fresh history returns every level (companies, postings, share, rank) with null deltas and no events. Run it again — on a schedule, weekly is the natural cadence — and every row gains a run-over-run delta, a trailing median and a breakout flag. History is kept in a named key-value store (stateStoreName), because Apify recreates the default store on every run.

Input

Every field has a working default. Running it with no configuration at all scans a twelve-company watchlist and returns a real index.

FieldTypeDefaultNotes
greenhouseBoardsarray of strings7 companiesSlug is the last path segment of job-boards.greenhouse.io/anthropic → anthropic. A pasted URL is accepted and reduced to its slug.
ashbyBoardsarray of strings4 companiesSlug is the first path segment of jobs.ashbyhq.com/openai → openai.
leverBoardsarray of strings["palantir"]jobs.lever.co/palantir → palantir.
categoriesarray of enums[] (all 14)language, frontend, mobile, backend, data-platform, database, cloud, devops, observability, ai-ml, security, testing, saas-and-business, practice.
excludeSelfMentionsbooleantrueStops a vendor's own board from counting its own product. See "How the counting works".
minCompaniesPerTechnologyinteger1Raise to cut the long tail and the cost.
minPostingsPerTechnologyinteger1Same, by postings.
maxIndexRowsinteger0 (no cap)Rows are ordered by postings, so 50 gives the 50 most-demanded technologies. Hard cap on cost.
emitEventsbooleantrueWrites to the named technology-events dataset.
maxEventsinteger500Cost guard. 0 means no cap. Events are ranked breakouts → adoptions → abandonments, so a cap keeps the informative ones.
trailingRunsinteger12 (2–104)Window for the trailing median and the window delta. At 12 on a weekly schedule this is the 12-week delta. Also how much history is stored.
breakoutThresholdPercentinteger150 (105–1000)Share must reach this percentage of the trailing median to flag a breakout.
minMentionsForBreakoutinteger3Stops 1 → 3 postings looking like a trend.
minRunsForBreakoutinteger4 (capped at trailingRuns)How much history a technology needs before a breakout can fire.
stateStoreNamestringtechnology-demand-index-stateNamed key-value store holding the history. Use a different name to keep two watchlists as two independent series.
resetStatebooleanfalseWipes the history and starts a new baseline.
maxPostingsPerBoardinteger0 (no limit)Caps postings read per company. Changing it changes the denominator — keep it steady across runs.
maxConcurrencyinteger5 (1–20)One request returns an entire board.
proxyConfigurationobjectno proxyNot needed; these endpoints have no anti-bot protection.

Output

Index rows — the default dataset

One row per technology per run. A real record from a run over Anthropic, Cloudflare, Ramp, Vanta and Palantir (1,570 postings):

{
"runId": "local-2026-09-23T23:37:32.627Z",
"runAt": "2026-09-23T23:37:32.627Z",
"technology": "Kubernetes",
"category": "devops",
"rank": 13,
"companiesRequiring": 3,
"companiesScanned": 5,
"companySharePct": 60,
"postingsMentioning": 147,
"postingsScanned": 1570,
"postingSharePct": 9.36,
"deltaBasis": "same-watchlist",
"previousRunAt": "2026-09-23T23:37:27.752Z",
"previousCompaniesRequiring": 3,
"companiesRequiringDelta": 0,
"previousPostingSharePct": 9.36,
"postingSharePctDelta": 0,
"windowRuns": 1,
"windowStartAt": "2026-09-23T23:37:27.752Z",
"postingSharePctAtWindowStart": 9.36,
"postingSharePctWindowDelta": 0,
"trailingMedianPostingSharePct": 9.36,
"shareVsTrailingMedian": 1,
"isBreakout": false,
"newCompaniesThisRun": [],
"droppedCompaniesThisRun": [],
"firstSeenInIndexAt": "2026-09-23T23:37:27.752Z",
"runsInHistory": 1,
"exampleCompanies": [
"anthropic"
],
"exampleJobUrls": [
"https://job-boards.greenhouse.io/anthropic/jobs/5411560008",
"https://job-boards.greenhouse.io/anthropic/jobs/5411267008",
"https://job-boards.greenhouse.io/anthropic/jobs/4610158008"
],
"vocabularyVersion": "2026.09.1"
}

Field notes worth reading:

  • postingSharePct is the headline number: the percentage of all postings in your watchlist that mention this technology. postingSharePctDelta is in percentage points, not percent.
  • deltaBasis tells you whether the comparison is honest: same-watchlist means the previous run covered exactly the same companies, watchlist-changed means it did not (adding one company moves every share), first-run means there is no baseline yet.
  • windowRuns is how many prior runs actually backed the trailing median. It is small until you have run the actor a few times.
  • newCompaniesThisRun / droppedCompaniesThisRun name the companies behind companiesRequiringDelta.

Events — the named technology-events dataset

Three event types. This dataset is append-only across runs — it is the event log, and it is not purged between runs, so you can read the whole history from one place.

{
"eventType": "technology_mention_share_breakout",
"technology": "Distributed systems",
"category": "practice",
"companySlug": null,
"postingsMentioning": 246,
"previousPostingsMentioning": 16,
"postingSharePct": 15.67,
"trailingMedianPostingSharePct": 8,
"summary": "Distributed systems is mentioned in 15.67% of postings across the watchlist, against a trailing median of 8%."
}
  • technology_first_required — a company that was scanned before now has at least one open posting requiring this technology, and had none last time. companySlug, source, exampleJobTitle and exampleJobUrl point at that company's own posting.
  • technology_no_longer_required — the reverse. Only fires for companies whose board was actually read both times, so a board that 404s is never mistaken for an abandonment.
  • technology_mention_share_breakout — index-level, so companySlug is null.

A company scanned for the first time produces a baseline, not events — otherwise adding one company to the watchlist would emit two hundred fake adoptions.

Key-value records

SUMMARY (run counters), COVERAGE (per board: reachable, postings read, technologies detected, self-mentions suppressed) and EVENTS (this run's events).

How the counting works

  • One posting counts once per technology. A posting that says "Python" nine times is one Python posting. The index measures how many roles require a technology, not how loudly.
  • Whole-token matching, longest match first. "Java" never fires on "JavaScript"; "Apache Spark" beats "Spark"; "React Native" beats "React". Separators employers use between technologies (/, -, ,) split, so "Python/Go/Rust" counts as three.
  • Guards on ambiguous names. Short or English-word names — Go, R, C, React, Swift, Spark, Chef, Excel, Linear, Notion — require the exact capitalisation plus nearby supporting context, and are vetoed by phrases like "go-to-market", "R&D", "Series C" or "head chef". This biases toward undercounting: a genuine mention next to an unlucky phrase is dropped rather than a false one admitted.
  • Sub-products credit their parent. "AWS Lambda" also counts as AWS, "BigQuery" also counts as Google Cloud Platform, "T-SQL" also counts as SQL.
  • Self-mentions are dropped by default. Stripe's postings all say "customers use Stripe to accept payments" and Databricks mentions Databricks in roughly a third of its ads. Counted naively, every vendor becomes its own biggest adopter. With excludeSelfMentions on, a board whose slug or company name matches a technology name (or is a prefix of one, so openai also drops "OpenAI API") does not contribute to that technology. COVERAGE lists what was suppressed per board.

Cost

Pay-per-event. See MONETIZATION.md for the full plan; the shape is:

EventPriceWhen
index-row$0.010Each technology row delivered
adoption-event$0.020Each company-level or breakout event delivered
board-scanned$0.005Each board successfully read

A default run — the 12-company watchlist, measured at 4,626 postings — produces 280 index rows: $2.80 plus $0.06 in board fees, $2.86, and a few dollars of events once history exists. A 300-company watchlist produces roughly 350–400 rows: about $3.75 + $1.50 = $5.25 per run, so weekly monitoring of 300 accounts costs roughly $21/month.

To spend less, set maxIndexRows to the number of technologies you actually track (maxIndexRows: 50 costs $0.50 plus board fees) or raise minCompaniesPerTechnology to drop the long tail.

Charges are applied in value order — index rows first, then events, then board fees — so a tight ACTOR_MAX_TOTAL_CHARGE_USD buys you the index rather than being spent on overhead. When the cap is reached the run stops cleanly, still saves its history, and reports chargeLimitReached: true in SUMMARY.

Known limitations

Read these before you run it on a schedule.

  • The first run has no deltas. By design. It reports levels and writes the baseline. Events need a second run; the trailing median and breakout flags need minRunsForBreakout (default 4) runs.
  • A job ad is not a technology audit. This measures what companies say they are hiring for, which leads actual adoption and lags actual abandonment. A company that quietly runs Kafka everywhere but is not hiring for it will not show Kafka. Treat it as a demand signal, not an inventory.
  • Changing the watchlist breaks comparability. Every share is a fraction of all postings in the watchlist, so adding or removing companies moves every row. The actor flags this in deltaBasis rather than hiding it, but the honest move after a big change is resetState: true.
  • Changing maxPostingsPerBoard does the same thing, for the same reason.
  • Board size dominates the weights. A watchlist with one 900-posting employer and nine 40-posting ones is largely an index of that one employer. companiesRequiring / companySharePct are the board-size-independent columns; use them when that matters.
  • The vocabulary is curated, not exhaustive. 395 technologies, chosen for what shows up in job ads. Something niche to your sector may simply not be in it. vocabularyVersion ships on every row so you can tell when the list changed underneath a series.
  • Guarded names undercount. See "How the counting works". Go, R and C in particular are deliberately conservative.
  • Lever boards 404 often. Many companies that once used Lever have moved. A 404 is logged as a warning and skipped, never retried and never counted as an abandonment. The run only fails if every board fails.
  • No pagination, no deduplication across boards. One GET returns one whole board. A company listing the same role on two boards is counted twice.
  • companyName is only populated for Greenhouse. Ashby and Lever do not return it on these endpoints; use companySlug.
  • History lives in your account. If you delete the named key-value store, or run the actor under a different stateStoreName, the next run is a fresh baseline.
  • Company-level facts only. These feeds carry no recruiter, hiring-manager or applicant fields and this actor reads none of them.

This actor is not affiliated with, endorsed by, or connected to Greenhouse, Ashby, Lever, or any of the companies whose job boards it reads.

It reads public, unauthenticated JSON endpoints that these applicant tracking systems expose to render their customers' public job boards (boards-api.greenhouse.io, api.ashbyhq.com/posting-api, api.lever.co/v0/postings). No login, key or token is used, and no access control is circumvented. Even so: "public" and "permitted by the Terms of Service" are different questions. These vendors' terms, and the terms of the sites that embed these boards, very likely restrict automated access and bulk collection regardless of whether the data requires a login. Using this actor may not comply with them.

You are responsible for your own compliance — with those terms, with the rate at which you run this actor, and with the laws that apply where you operate — and for what you do with the data it returns. If you are building a commercial product on this output, get your own legal advice rather than relying on the fact that a URL returned HTTP 200.

The endpoints can change or be withdrawn without notice; they are internal APIs, not a documented public product.