datagrit

datagrit › Jobs & salaries › Hacker News Who Is Hiring: Jobs & Hiring History

Jobs & salaries

Hacker News Who Is Hiring: Jobs & Hiring History

Turn Hacker News Who is hiring threads into job rows with salary, stack, work mode and visa, plus how many months each company has been hiring.

Run it on Apify StoreUse the APIfrom $3.15 per 1,000 results + $10 per run · no code needed
from $3.15 per 1,000 results + $10 per runpay only for job posts you get
JSON · CSV · Excelexport or call via API
Scheduled runsdaily or weekly feeds with Apify schedules
v0.7updated 2026-10-04

Hacker News Who Is Hiring: Jobs & Hiring History turns the monthly "Ask HN: Who is hiring?" thread into a clean table of job posts. Every top-level comment becomes one row with the company, roles, location, work mode, employment type, stated pay, visa sponsorship, technologies, apply link and the full post text. What other scrapers of this thread do not give you is company history: the Actor compares every company with the earlier monthly threads and tells you how many months it has been hiring, when it first and last posted before, and whether it is posting for the first time. It reads the public Hacker News search API, needs no login or API key, and exports to JSON, CSV or Excel.

Who is it for?

How to use it

  1. Choose which thread to read: the latest one, the last few months, or one specific month such as 2026-03.
  2. Set how many earlier months each company is compared against. Twelve months is the default. Zero switches history off and makes the run faster.
  3. Add filters if you want a narrower list (below), set the maximum number of posts and run the Actor.

Filters

Company history

For each post the Actor normalises the company name (lower case, letters and digits only, legal suffixes removed) and, when the post links the company's own website, also keeps the domain (acme.com for Acme, factory.ai for Factory AI). It then looks for the name or the domain in the earlier threads, so a company that links its site in one month and not in the next is still one company. A link to a news article, a LinkedIn short link or a file host inside the post text is never taken for the company's domain. The companyKey column is the normalised name alone (acme, factoryai), so it does not change with the link; two spellings of one company (Factory and Factory AI) give two keys, and the history columns still treat them as one when the website domain is the same. You get monthsHiring (this month included), firstSeenMonth, previousSeenMonth, consecutiveMonths, isNewCompany and postsThisMonth. Posts where no company could be read from the header, or only a placeholder such as Stealth, Confidential or Startup, have no key and carry null history, monthsChecked included, rather than a guess. A header that starts with a place (Boston, MA - Full Time, NYC | Engineer; a name with a place after a comma, such as adapptiv Labs, Switzerland or LiquidFi (Miami, FL), stays a company), a role (Software Engineer | Acme) or an employment phrase gives no company either, and those rows are delivered free of charge, as are rows with a placeholder name. Older threads (before about 2016) were written free-form, so many of their posts have no readable company.

History is only computed from threads whose posts can be read reliably: every thread of the window, and the thread you ask for, must have a standard Company | role | location header in at least 75% of its posts. Before 2016 that is not the case (see "Good to know"), so a run that asks for history on such a month, or on a month whose window reaches one, stops with a message instead of returning rows with empty history. Set Months of history to 0 to read those months as plain posts: you then get the posts without history fields, and rows without a company name are free.

Pricing

You pay per job post returned. Posts removed by your filters, posts skipped as already delivered, posts without a usable company name, placeholders such as Stealth included (the run status counts them) and a run that finds nothing are not charged: a search without matches returns a single status row with found: false that is free. Reading earlier threads for the history costs nothing extra. Set a maximum charge per run in the Apify console to cap spending; the Actor stops cleanly when it is reached.

Good to know

Output fields

Every result is one flat record, so it drops straight into a spreadsheet, a database or a CRM.

FieldTypeDescriptionExample
foundbooleanFalse only on the single status row returned when nothing matched your filters. Normal rows do not carry this field.false
monthstringStatus row only: the month you asked for in the Month input, or null when you asked for the latest threads.2026-09
postIdstringHacker News comment ID of the job post. Unique, stable and safe to use as a key.49522903
sourceUrlstringLink to the job post on news.ycombinator.com, where replies and the author profile live.https://news.ycombinator.com/item?id=49522903
threadIdstringHacker News ID of the monthly Who is hiring thread the post belongs to.49522897
threadMonthstringMonth of the thread as YYYY-MM, taken from the thread title.2026-09
postedAtstringWhen the post was written, ISO 8601 in UTC.2026-09-01T15:01:54.000Z
authorstringHacker News username of the person who wrote the post. Usually a founder or recruiter of the company.jobposter
companystringCompany name read from the first part of the header line, without funding or batch notes. Null when the post has no header in the usual Company | role | place form, when that first part is a place, a role or an employment phrase, or when it is a placeholder such as Stealth or Confidential (the name is kept but there is no company key); such rows are delivered free of charge.Modash.io
companyWebsitestringCompany homepage: the URL in the header, or a link in the post whose domain carries the company name (not a news article, social or file-host link). Null when the post has none.https://modash.io
companyKeystringThe normalised company name: lower case, letters and digits only, legal suffixes such as Inc or GmbH removed. It is built from the name alone, so the same spelling always gives the same key whether or not the post links a website. Two different spellings of one company (Factory and Factory AI) give two keys; the history columns still match them through the website domain. Null when the post has no company name.modashio
headerLinestringFirst line of the post exactly as written, with links shown as plain URLs. It is where company, roles, place and pay usually sit.Modash.io | Senior Product Engineer | Remote (Europe) | Full-time | €75k–110k
headerParsedbooleanTrue when the header had the usual "Company | role | location" form with a company name. False means roles and location are best guesses or empty.true
rolesarrayJob titles or role groups listed in the header. Empty when the header names none.["Senior Product Engineer"]
locationstringPlace or region from the header, for example "Remote (Europe)" or "London, UK". Null when the header gives none.Remote (Europe)
workModesarrayAny of remote, hybrid, onsite found in the header, or in the start of the post when the header says nothing.["remote"]
employmentTypesarrayAny of full-time, part-time, contract, freelance, internship, temporary found in the header.["full-time"]
salaryTextstringThe pay segment exactly as written, cut at 120 characters. Null when the post states no pay in the header or in a Salary line.€75k–110k
salaryMinnumberLower end of the stated pay in the currency and period given. Equal to the maximum when a single figure is stated.75000
salaryMaxnumberUpper end of the stated pay in the currency and period given.110000
salaryCurrencystringISO currency code (USD, EUR, GBP and so on) inferred from the symbol or code next to the figures. Null when it cannot be told.EUR
salaryPeriodstringyear, month, day or hour. Figures of 1,000 or more with no period are read as yearly.year
salaryYearlyMinnumberSalary minimum converted to a year (monthly x12, daily x220, hourly x2080) so posts can be compared. Still in the posted currency; null without a known period.75000
salaryYearlyMaxnumberSalary maximum converted to a year the same way as the yearly minimum.110000
visaSponsorshipbooleanTrue when the post offers visa or work-permit sponsorship, false when it says it does not, null when it does not mention it.true
technologiesarrayLanguages, frameworks, databases and cloud tools named anywhere in the post, from a fixed list of about 100 names.["TypeScript","PostgreSQL"]
applyUrlstringFirst link in the post that points to a job page or an applicant tracking system. Null when there is none.https://jobs.ashbyhq.com/modash/123
linksarrayEvery link found in the post.["https://modash.io","https://jobs.ashbyhq.com/modash/123"]
emailsarrayEmail addresses written in the post, lower-case.["jobs@modash.io"]
descriptionTextstringFull text of the post with HTML removed; paragraphs are separated by blank lines.Modash.io | Senior Product Engineer | Remote (Europe) | Full-time Modash helps
monthsCheckedintegerNumber of monthly threads compared for company history, this one included. At most Months of history plus one; fewer when HN has no thread for some months (the run status then lists the missing months). Null when history is off or when the row has no company key (no earlier thread was compared).13
monthsHiringintegerIn how many of the checked threads the company posted, this one included. Null when history is off or the post has no company key.7
firstSeenMonthstringEarliest checked month in which the company posted (YYYY-MM). Equals the thread month for a company first seen now. Null when history is off or the post has no company key.2026-01
previousSeenMonthstringLatest earlier month in which the company posted (YYYY-MM). Null for a company first seen now, when history is off or when the post has no company key.2026-08
consecutiveMonthsintegerHow many months in a row, counting back from this thread, the company has posted. Counted only within the months checked. A month in which Hacker News published no thread is skipped without ending the streak; a month with a thread in which the company did not post ends it. Null when history is off or the post has no company key.3
isNewCompanybooleanTrue when the company did not post in any of the earlier checked threads. Null when history is off or the post has no company key.false
postsThisMonthintegerHow many posts the company has in this thread; some companies post one per role. Null when history is off or the post has no company key.1
scrapedAtstringWhen the row was produced, ISO 8601 in UTC.2026-10-01T06:00:00.000Z

Sample record

{
  "found": false,
  "month": "2026-09",
  "postId": "49522903",
  "sourceUrl": "https://news.ycombinator.com/item?id=49522903",
  "threadId": "49522897",
  "threadMonth": "2026-09",
  "postedAt": "2026-09-01T15:01:54.000Z",
  "author": "jobposter",
  "company": "Modash.io",
  "companyWebsite": "https://modash.io",
  "companyKey": "modashio",
  "headerLine": "Modash.io | Senior Product Engineer | Remote (Europe) | Full-time | €75k–110k",
  "headerParsed": true,
  "roles": [
    "Senior Product Engineer"
  ],
  "location": "Remote (Europe)",
  "workModes": [
    "remote"
  ],
  "employmentTypes": [
    "full-time"
  ],
  "salaryText": "€75k–110k",
  "salaryMin": 75000,
  "salaryMax": 110000,
  "salaryCurrency": "EUR",
  "salaryPeriod": "year",
  "salaryYearlyMin": 75000,
  "salaryYearlyMax": 110000,
  "visaSponsorship": true,
  "technologies": [
    "TypeScript",
    "PostgreSQL"
  ],
  "applyUrl": "https://jobs.ashbyhq.com/modash/123",
  "links": [
    "https://modash.io",
    "https://jobs.ashbyhq.com/modash/123"
  ],
  "emails": [
    "jobs@modash.io"
  ],
  "descriptionText": "Modash.io | Senior Product Engineer | Remote (Europe) | Full-time\n\nModash helps brands find and manage creators.",
  "monthsChecked": 13,
  "monthsHiring": 7,
  "firstSeenMonth": "2026-01",
  "previousSeenMonth": "2026-08",
  "consecutiveMonths": 3,
  "isNewCompany": false,
  "postsThisMonth": 1,
  "scrapedAt": "2026-10-01T06:00:00.000Z"
}

Input

FieldNameTypeWhat it does
monthsBackLatest threads to readintegerHow many of the most recent monthly threads to read, newest first. 1 is the current month. Ignored when Month is set. A thread with fewer than 10 comments (the first hours of a month) is skipped.
monthSpecific monthstringRead one month instead, written as YYYY-MM, for example 2026-03. The run fails when neither the whoishiring accounts nor a title search on Hacker News finds a Who is hiring thread for that month. Before 2016 most posts have no standard header, so such a month needs Months of history set to 0.
historyMonthsMonths of historyintegerHow many earlier monthly threads to compare every company against, for months hiring, first seen, previous seen and new company. 0 switches history off and makes the run faster; it is also the setting for months before December 2016, where company history cannot be computed and a run with history fails. More months mean more threads to download: each one adds about a second.
keywordsPost text containsarrayKeep posts whose full text contains at least one of these words or phrases (case-insensitive), for example climate or "machine learning".
roleContainsRole containsarrayKeep posts whose header line contains at least one of these words, for example designer or "data engineer". The header is where roles are listed.
locationContainsLocation containsarrayKeep posts whose header line contains at least one of these words, for example berlin, europe or "new york".
technologiesTechnologies (any of)arrayKeep posts that name at least one of these technologies anywhere in the text, for example python, rust or kubernetes. Names are matched against the Technologies column, case-insensitive.
workModeWork modestringKeep posts whose header mentions this mode. Posts that do not state a mode are dropped when you choose one.
employmentTypeEmployment typestringKeep posts whose header mentions this type. Posts that do not state one are dropped when you choose one.
onlyWithSalaryOnly posts with a salarybooleanKeep only posts that state pay in the header or in a Salary line.
minSalaryMinimum yearly salaryintegerKeep posts whose top stated pay, converted to a year, is at least this amount. Needs Salary currency, because pay is posted in different currencies. 0 means no minimum.
salaryCurrencySalary currencystringCurrency that Minimum yearly salary is measured in. Posts in other currencies are dropped when a minimum is set.
visaSponsorshipOnlyOnly with visa sponsorshipbooleanKeep only posts that offer visa or work-permit sponsorship.
onlyNewCompaniesOnly first-time companiesbooleanKeep only companies that did not post in any of the earlier threads covered by Months of history. Needs history.
minMonthsHiringMinimum months hiringintegerKeep companies that posted in at least this many of the checked threads, this one included. Use it to find companies that hire all the time. Needs history. 0 means no minimum.
onlyNewSinceLastRunOnly posts not delivered beforebooleanReturn only posts that no earlier run with the same filters has delivered. Schedule the Actor during the month to get each new post once. Runs with different filters keep separate memory.
maxItemsMaximum postsintegerStop after this many posts in total across all threads read. A monthly thread holds 250 to 450 posts.
proxyConfigurationProxy configurationobjectOptional proxy. Leave disabled unless the Hacker News search API rate-limits the shared platform IP (HTTP 429); a proxy raises the platform cost of the run.

Call it from your code

Run the Actor and get the results in one request. Replace YOUR_APIFY_TOKEN with the token from your Apify account settings.

curl -X POST "https://api.apify.com/v2/acts/datagrit~hacker-news-hiring-history/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"monthsBack":1,"historyMonths":12,"technologies":["python"],"maxItems":100}'
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('datagrit/hacker-news-hiring-history').call({
  "monthsBack": 1,
  "historyMonths": 12,
  "technologies": [
    "python"
  ],
  "maxItems": 100
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.length, items[0]);

Install with npm i apify-client.

from apify_client import ApifyClient
import os

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("datagrit/hacker-news-hiring-history").call(run_input={
  "monthsBack": 1,
  "historyMonths": 12,
  "technologies": [
    "python"
  ],
  "maxItems": 100
})
items = client.dataset(run["defaultDatasetId"]).list_items().items
print(len(items), items[0] if items else None)

Install with pip install apify-client.

Frequently asked questions

Is it legal to scrape Hacker News?

The Actor reads the public Algolia search API that Hacker News itself links to, for public job posts. It collects no data behind a login.

How often is the thread published?

Once a month, on the first working day. Schedule the Actor weekly with "Only posts not delivered before" switched on to collect the new posts as they appear during the month.

How far back does the history go?

Up to 36 earlier months, as long as those threads are readable (see Good to know). Each earlier thread adds about two seconds to the run.

Why is a company missing from the history?

Its header gave no company name, or it posted under a different name. Rows without a company key have null history.

The run failed with HTTP 429 or a message about the waiting budget.

The Hacker News search API limits requests per IP address and the Actor shares an IP address with other runs on the platform. Failed attempts and pauses between attempts draw from one 75-second budget for the whole run, and a request that gets no answer is cut off after 25 seconds (also when a proxy accepts the connection and stays silent). When the budget is used up the Actor stops with an error instead of running past the five-minute limit of a scheduled test; successful requests do not use it. Run again in a few minutes, or switch on the proxy in the input.

Something looks wrong in the data.

Open an issue on the Actor page with the input you used and the run link.

Try Hacker News Who Is Hiring: Jobs & Hiring History on Apify

Related Actors

Jobs & salaries

Career Site Jobs Aggregator - ATS Salary Data

Open jobs from Greenhouse, Lever, Ashby, Workday, Workable, SmartRecruiters, Recruitee and Personio in one schema with normalized salaries.

from $2.80 / 1,000 results