# Hacker News Who Is Hiring Scraper - Structured Jobs (`signalscope-data/hn-hiring-parser`) Actor

Parse HN 'Who is hiring?' and the HN jobs board (YC ads with salary & equity) into structured jobs: tech stack, remote, seniority, company leads, trends, plus a live still-hiring check. $2 / 1,000 results.

- **URL**: https://apify.com/signalscope-data/hn-hiring-parser.md
- **Developed by:** [Bin](https://apify.com/signalscope-data) (community)
- **Categories:** Jobs, Lead generation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 job posts

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Hacker News Who Is Hiring Scraper - Structured Jobs

Turn the monthly **Hacker News "Ask HN: Who is hiring?"** threads into **clean, structured job data**: company, roles, locations, remote / onsite / hybrid, visa sponsorship, **parsed salary (min, max, currency, period)**, **tech stack**, apply URL and more. Run it once for a dataset, or on a schedule with **new-posts-only monitoring** so you only get (and pay for) fresh posts. Also covers the **HN jobs board** (news.ycombinator.com/jobs): the "Acme (YC W24) Is Hiring" ads that YC startups post, with **salary, equity %, visa, minimum experience, skills and company facts** from the linked job page. Plus a **hiring-market trends report** (technology demand, salaries, remote share, month over month) for the whole thread or any segment such as "remote Rust jobs".

No login, no API key. It uses the public HN Algolia API, with the official HN Firebase API as a fallback.

### Why this one

Most HN hiring scrapers give you the raw comment text. This Actor parses each post properly:

- 💰 **Salary parsing**: `$150k-$200k`, `€60-80k`, `$150 - 210K USD`, `£80,000-150,000`, `$120-160/hr`, `€6,000 - €7,000 per month` become `salaryMin`, `salaryMax`, `salaryCurrency` and `salaryPeriod` (year / month / hour). Funding rounds ("$42M Series B") and 401(k) are ignored.
- 🧰 **Tech stack**: matched against ~170 technologies (languages, frameworks, databases, cloud, ML, infra), e.g. `["TypeScript", "React", "PostgreSQL", "AWS", "Kubernetes"]`.
- 🌍 **Workplace and location**: `remote`, `onsite` and `hybrid` flags plus normalized `locations` (`"San Francisco, CA"`, `"Europe"`, `"Toronto, ON"`).
- 🛂 **Visa sponsorship**: `true` / `false` / `null`, and it reads phrases like "no visa sponsorship" or "we sponsor visas".
- 🏢 **Company, roles, employment type, YC batch, company URL and apply URL** from the `Company | Role | Location | ...` header plus fallbacks for free-form posts.
- 🎯 **Seniority, role category and company stage** (new): `seniority` (intern, junior, mid, senior, staff, principal, lead, founding, manager, director, executive), `roleCategories` (backend, frontend, full-stack, mobile, ML/AI, data, DevOps/SRE, security, embedded, design, product, sales, marketing, operations…), `equityMentioned`, `benefits` (health insurance, 401k, unlimited PTO, 4-day week, parental leave, learning budget…), `fundingStage` (pre-seed → Series F, public, bootstrapped), `fundingRaised` ("$14M"), `profitable` and `teamSize`. Filter on all of them: e.g. **senior ML jobs at Series A startups with equity**.
- 📈 **Company hiring history**: for every post, see how many of the last N monthly threads the company posted in (`companyMonthsHiring`), its consecutive-month `companyHiringStreak`, `companyFirstSeenMonth`, the list of months, and `isNewToHnHiring`. Spot companies that are **hiring for the first time** (often freshly funded) or **hiring every month** (fast growth or hard-to-fill roles).
- 🏢 **Company lead list** (new): set `outputFormat: "companies"` to get **one row per hiring company** instead of one per post: post count, months posted, all roles, locations, tech stack, yearly salary range, apply links and a `hiringStatus` (`new` / `returning` / `consistent` / `recent`). Ready for a CRM import.
- 📊 **Market trends report** (new): `includeTrends` (or `trendsOnly`) adds one `market-trends` row per month: how many posts match your filters and their share of all posts, remote / visa / equity share, **salary p25 / median / p75 per currency**, the **top 30 technologies with rank and share change vs the previous month**, role categories, seniority, top locations and funding stages — plus a `market-trends-summary` row with **rising and falling technologies** across the window. Rule-based, reproducible, no AI.
- 🚀 **HN jobs board / YC startup jobs** (new): `includeJobBoard` (or `jobBoardOnly`) adds the job ads YC companies post on news.ycombinator.com/jobs (about 40–60 a month, history back to 2010 with `jobBoardDays`). Each ad gets company, `ycBatch`, role, location, remote — and the Actor reads the linked job page: **YC job pages** give salary range, **equity range (`equityMinPct` / `equityMaxPct`)**, `visaRequirement`, `minExperience`, `skills`, team size, founding year, one-liner and tags; **Greenhouse, Ashby and Lever** links are read through their public job APIs; other career pages through schema.org JobPosting data. `jobListingOpen` tells you whether the job is still listed (removed jobs return `false`). Same filters, same price.
- ✅ **Still hiring? Live open-role check** (new): every post gets `atsProvider`, `atsBoard` and `atsBoardUrl` when it links a job board (Greenhouse, Lever, Ashby, Workable, SmartRecruiters, Recruitee, Workday, Rippling, BambooHR, Breezy, Pinpoint, YC Work at a Startup…). Turn on `verifyOpenRoles` and the Actor also opens the company's careers page to find its job board when the post links only the website, then reads the board **right now**: `stillHiring`, `liveOpenRoles`, `liveMatchingRoles` (open roles in the same categories as the post), `liveRemoteRoles`, `liveRolesSincePost`, `liveRolesLast30Days` and up to 15 `liveRoleTitles`. `onlyStillHiring` drops companies that have stopped hiring — ideal for older threads and lead lists. One request per company, no extra charge.
- 🔔 **Monitor mode**: posts keep arriving all month. With `onlyNewPosts` the Actor remembers what it already returned, so a daily schedule gives you only new posts. You never pay twice for the same post.
- 🧹 **Meta comments are skipped** ("there used to be more posts here…"), and you are not charged for them.
- 🔒 **Privacy-safe**: email addresses are never extracted and are masked in the text. The `applyByEmail` flag tells you that the post has an email contact, which you can see in the linked HN post.

Parse quality measured on 6 recent months (1,550 job posts): company **97%**, workplace (remote/onsite/hybrid) **92%**, apply URL **89%**, roles **81%**, locations **80%**, tech stack **71%**, salary **32%**. These figures count only posts that state the value: about a third of HN posts publish a salary range, and nearly all of those ranges are parsed. In the October 2026 thread (212 job posts): role category **86%**, seniority **47%** (the rest don't state a level), equity mentioned **23%**, funding stage **13%**, benefits **11%** — again, only posts that say it.

### Use cases

- **Job seekers**: get a daily feed of remote Rust/Go/TypeScript jobs above $150k in your inbox, Slack or Google Sheet.
- **Recruiters and sales (lead generation)**: find which startups are hiring right now, what they pay and what stack they use.
- **Market research and content**: a ready-made monthly report of technology demand, salary ranges and remote share (whole market or your niche) for newsletters, blog posts, investor memos or hiring plans.
- **Job boards and newsletters**: import structured HN jobs automatically.
- **YC startup jobs feed**: every new "(YC …) Is Hiring" ad with salary and equity, filtered to your stack, delivered daily — with a flag when a listing closes.
- **AI / RAG datasets**: clean, labeled job data for analysis.

### Input example

```json
{
  "months": 1,
  "remoteOnly": true,
  "techs": ["Rust", "TypeScript"],
  "minSalary": 150000,
  "keywords": ["founding engineer"],
  "locations": ["US", "Europe"],
  "onlyNewPosts": true,
  "stateKey": "remote-rust-ts"
}
```

- `months`: number of latest monthly threads (default 1). Or use `threadUrls` for specific threads, e.g. `https://news.ycombinator.com/item?id=49922569`.
- `includeWhoWantsToBeHired` / `includeFreelancer`: also parse candidate threads (see below).
- `keywords`, `remoteOnly`, `locations`, `techs`, `minSalary`: filters (ANY match within each list).
- `onlyNewPosts` + `stateKey`: monitoring. Use one `stateKey` per schedule.
- `seniority`, `roleCategories`, `companyStages`, `equityOnly`: filter by level, role type, funding stage (`series` = any Series round, `profitable`) and equity.
- `hiringHistoryMonths`: add company hiring history over the last N months (e.g. 12). No extra charge.
- `onlyNewCompanies`: output only companies posting for the first time in the history window.
- `outputFormat`: `"posts"` (default, one row per job post) or `"companies"` (one row per company).
- `maxItems`: cap results (and cost).
- Keywords match at the start of a word: `rust` finds "Rust" and "Rust-based" but not "trust".

#### Lead-generation example: startups hiring on HN for the first time

```json
{
  "months": 1,
  "hiringHistoryMonths": 12,
  "onlyNewCompanies": true,
  "onlyNewPosts": true,
  "stateKey": "first-time-hirers"
}
```

In October 2026 this returned ~90 of ~190 companies: about half of the companies in a typical month did not post in the previous 11 threads.

#### Senior / staff ML & AI jobs at funded startups with equity

```json
{
  "months": 1,
  "seniority": ["senior", "staff", "principal"],
  "roleCategories": ["ml-ai"],
  "companyStages": ["seed", "series"],
  "equityOnly": true
}
```

`roleCategories` alone (`["ml-ai"]`, `["devops-infra"]`, `["design"]`…) is the quickest way to cut a 200-post thread down to the jobs you care about without guessing keywords.

#### Company lead list: who is hiring Rust engineers, one row per company

```json
{
  "months": 3,
  "techs": ["Rust"],
  "outputFormat": "companies",
  "hiringHistoryMonths": 12
}
```

Three months of threads (≈700 posts) → 46 companies, each with their roles, stack, salary range and hiring status. Example row:

```json
{
  "recordType": "company",
  "company": "SerpApi",
  "companyUrl": "https://serpapi.com",
  "postCount": 2,
  "monthsPosted": [
    "October 2026",
    "September 2026"
  ],
  "latestPostUrl": "https://news.ycombinator.com/item?id=49929200",
  "roles": [
    "Junior to Senior Fullstack Engineer multiple positions",
    "Customer Success Engineer",
    "Hiring Coordinator"
  ],
  "locations": [
    "Austin"
  ],
  "remote": true,
  "techStack": [
    "Python",
    "Rust",
    "C#",
    "Ruby",
    "Ruby on Rails",
    "PHP"
  ],
  "salaryMinYearly": 150000,
  "salaryMaxYearly": 180000,
  "salaryCurrency": "USD",
  "applyUrls": [
    "https://serpapi.com/careers"
  ],
  "companyMonthsHiring": 7,
  "companyHiringStreak": 2,
  "hiringStatus": "recent"
}
```

`hiringStatus` (needs `hiringHistoryMonths`): `new` = first post in the window, `consistent` = posted 3+ months in a row, `returning` = back after a gap, `recent` = posted in the previous month too (2-month streak). Salaries are converted to yearly (monthly × 12, hourly × 2,080) and use the currency of the most recent post.

#### YC startup jobs from the HN jobs board, with salary and equity

```json
{
  "jobBoardOnly": true,
  "jobBoardDays": 30,
  "remoteOnly": true,
  "onlyNewPosts": true,
  "stateKey": "yc-remote"
}
```

A real row (trimmed):

```json
{
  "recordType": "jobs-board",
  "company": "Gooseworks",
  "ycBatch": "W23",
  "roles": ["Founding Creative Engineer"],
  "locations": ["San Francisco, CA, US"],
  "remote": true,
  "salaryMin": 120000,
  "salaryMax": 200000,
  "salaryCurrency": "USD",
  "equityRange": "0.50% - 1.50%",
  "equityMinPct": 0.5,
  "equityMaxPct": 1.5,
  "minExperience": "Any (new grads ok)",
  "visaRequirement": "Will sponsor",
  "visaSponsorship": true,
  "seniority": ["founding"],
  "teamSize": 5,
  "companyOneLiner": "Self-Improving Ad Creative Engines",
  "companyYearFounded": 2022,
  "jobListingOpen": true,
  "detailsSource": "ycombinator",
  "applyUrl": "https://www.ycombinator.com/companies/gooseworks/jobs/rfgY8La-founding-creative-engineer",
  "postUrl": "https://news.ycombinator.com/item?id=49553173",
  "daysSincePosted": 33
}
```

In a test over the last 90 days (73 ads), 51% had a salary range, 45% came with YC page details, and 12 listings were already closed. Combine with `includeJobBoard: true` (instead of `jobBoardOnly`) to get thread posts and board ads in one dataset; `outputFormat: "companies"` merges both per company.

#### Hiring-market trends: technology demand and salaries, month over month

```json
{
  "months": 6,
  "trendsOnly": true
}
```

Add filters to analyse a segment, e.g. `"techs": ["Rust"]` or `"remoteOnly": true` (filters define which posts are counted; `allJobPosts` is always the whole thread). One `market-trends` row from a real run (October 2026, all posts, shortened):

```json
{
  "recordType": "market-trends",
  "month": "October 2026",
  "allJobPosts": 223,
  "matchingPosts": 223,
  "comparedToMonth": "September 2026",
  "matchingPostsChangePct": -10.8,
  "remotePct": 53.8,
  "salaryDisclosedPct": 35.9,
  "salaryByCurrency": { "USD": { "posts": 66, "p25Yearly": 155000, "medianYearly": 186250, "p75Yearly": 219000 } },
  "topTechs": [
    { "tech": "Python", "rank": 1, "posts": 53, "sharePct": 23.8, "prevRank": 1, "shareChangePts": -3.4 },
    { "tech": "React", "rank": 2, "posts": 51, "sharePct": 22.9, "prevRank": 3, "shareChangePts": 4.1 },
    { "tech": "TypeScript", "rank": 3, "posts": 49, "sharePct": 22, "prevRank": 2, "shareChangePts": 1.6 }
  ],
  "roleCategories": [{ "name": "software-engineering", "posts": 92, "sharePct": 41.3 }, { "name": "ml-ai", "posts": 48, "sharePct": 21.5 }],
  "topLocations": [{ "name": "US", "posts": 46, "sharePct": 20.6 }, { "name": "New York", "posts": 16, "sharePct": 7.2 }],
  "fundingStages": [{ "name": "series-a", "posts": 9, "sharePct": 4 }]
}
```

With 2+ months you also get a `market-trends-summary` row (older half of the window vs newer half): `postsByMonth`, change in posts, remote share and median USD salary, and `risingTechs` / `fallingTechs` / `risingRoleCategories` / `fallingRoleCategories` with share in each half. Example (Rust jobs, July–October 2026): rising Linux, AWS, C, Terraform; falling Distributed Systems, Go. The summary is also saved as the `TRENDS` record in the key-value store.

#### Companies still hiring Rust engineers (live check against their job boards)

```json
{
    "months": 6,
    "keywords": ["rust"],
    "outputFormat": "companies",
    "onlyStillHiring": true
}
```

Real result (October 2026): 45 matching posts → 27 companies; companies whose job board shows no open role are dropped. One row per company, for example:

```json
{
    "recordType": "company",
    "company": "Rerun",
    "postCount": 2,
    "latestPostMonth": "September 2026",
    "atsProvider": "ashby",
    "atsBoard": "rerun",
    "atsBoardUrl": "https://jobs.ashbyhq.com/rerun",
    "liveCheck": "ok",
    "stillHiring": true,
    "liveOpenRoles": 7,
    "liveMatchingRoles": 5,
    "liveRoleTitles": ["Software Engineer (Rust) - Backend", "Software Engineer, Infrastructure"]
}
```

In the October 2026 thread, 21% of posts link a job board directly; with `verifyOpenRoles` the careers pages of the others raise that to about 34% (75 boards checked, 74 found).

### Output example

```json
{
  "type": "job",
  "company": "Sidekick Labs",
  "roles": ["Senior SWE", "R&D Engineer", "Intelligence Engineer"],
  "locations": ["US"],
  "remote": true,
  "onsite": false,
  "hybrid": false,
  "visaSponsorship": null,
  "employmentType": ["full-time"],
  "salaryRaw": "$170k to $230k Base + Equity + Health",
  "salaryMin": 170000,
  "salaryMax": 230000,
  "salaryCurrency": "USD",
  "salaryPeriod": "year",
  "techStack": ["Python", "Go", "Microservices"],
  "applyUrl": "https://sidekicklabs.com",
  "companyUrl": "https://sidekicklabs.com",
  "urls": ["https://sidekicklabs.com"],
  "ycBatch": null,
  "seniority": ["senior"],
  "roleCategories": ["software-engineering", "research"],
  "equityMentioned": true,
  "benefits": ["health insurance"],
  "fundingStage": null,
  "fundingRaised": null,
  "profitable": false,
  "teamSize": null,
  "applyByEmail": true,
  "headline": "Sidekick Labs | Senior SWE, R&D Engineer, Intelligence Engineer | REMOTE (US Timezones) | Full-time | $170k to $230k Base + Equity + Health",
  "text": "Sidekick Labs | Senior SWE ... (full post as plain text, emails masked)",
  "postId": 49922609,
  "postUrl": "https://news.ycombinator.com/item?id=49922609",
  "postedAt": "2026-10-01T15:03:36.000Z",
  "threadId": 49922569,
  "threadTitle": "Ask HN: Who is hiring? (October 2026)",
  "threadMonth": "October 2026",
  "threadType": "hiring",
  "matchedKeywords": [],
  "companyMonthsHiring": 1,
  "companyHiringStreak": 1,
  "companyFirstSeenMonth": "October 2026",
  "companyHiringMonths": ["October 2026"],
  "isNewToHnHiring": true,
  "historyWindowMonths": 12
}
```

The `companyHiring*` fields appear only when `hiringHistoryMonths` (or `onlyNewCompanies`) is set. Companies are matched across months by normalized name and website domain.

A `SUMMARY` record in the key-value store lists threads processed, counts and parse-quality percentages for the run.

#### "Who wants to be hired?" and "Freelancer?" threads (optional)

Candidate posts (`type: "candidate"` / `"freelancer"`) contain `location`, `locations`, `remote`, `willingToRelocate`, `technologiesRaw`, `techStack` and a summary `text`. **All personal contact data is removed**: emails, phone numbers, résumé, LinkedIn, GitHub and other links. Use `postUrl` to view the post on HN.

### FAQ

**How is "new to HN hiring" decided?** The Actor parses every post in the previous monthly threads (up to 36 months) and matches companies by normalized name ("Acme, Inc." = "ACME") and website domain. If the company appears only in the current month within the window, `isNewToHnHiring` is `true`. Posts without a recognizable company get `null`.

**Does the history cost extra?** No. You pay only per returned post. History adds roughly 2–4 seconds of run time per month of history.

**Can I parse an old month?** Yes: put the thread URL in `threadUrls`. History is then computed relative to that month.

**Can I get one row per company instead of per post?** Yes: `outputFormat: "companies"`. All filters apply to the posts first; matching posts of the same company (same normalized name or website) are merged. Each company row is billed as one result, so it is also cheaper than per-post output.

**How are seniority and role category detected?** From the parsed role titles (e.g. "Staff ML Engineer" → `staff` + `ml-ai`). Posts without role titles use the start of the headline. A post hiring for several roles gets several values. "Multiple roles" posts get no seniority. Rule-based, no AI, so results are instant and identical on every run.

**How reliable is `fundingStage`?** It is set only when the post itself says it ("we just closed our Series A", "bootstrapped and profitable"). If several rounds are mentioned, the latest one wins. Most posts don't mention their stage, so `null` means "not stated", not "unfunded".

**How are the market trends calculated?** From every job post of each monthly thread (also in monitor mode, so the numbers describe the whole month, not only new posts) after your filters. Shares are % of matching posts; salaries are converted to yearly and each post counts with the midpoint of its range; a currency needs 3+ posts. The previous month is parsed for the comparison automatically and is not charged. Location spellings are merged ("USA" = "US", "SF" = "San Francisco Bay Area").

**What do the trend reports cost?** $0.01 per trend row: a 6-month report is 7 rows ($0.07). With `trendsOnly` no job rows are output or charged.

**What is the difference between the monthly thread and the HN jobs board?** The monthly "Who is hiring?" thread is free for any company and has 200–500 posts. The jobs board (news.ycombinator.com/jobs) is reserved for Y Combinator companies and has 40–60 ads a month, usually linking to a structured job page. The thread is better for volume, the board for YC startups with salary and equity data.

**Does reading the linked job pages cost extra?** No, it is included in the per-result price. Turn off `jobBoardDetails` for a faster title-only run. Pages that block automated visits are skipped (never bypassed) and get a `detailsError`.

**How does the live open-role check work?** Each post's apply link is matched against known job-board URLs (including shortlinks like grnh.se). If the post links only the company website or careers page, that page is opened once and searched for an embedded or linked job board. The board's public job list is then read once per company (cached across posts): `liveOpenRoles` is the number of jobs listed right now, `liveMatchingRoles` how many of them fall in the post's role categories (backend, ML/AI, design…). `liveCheck` is `ok`, `board-not-found`, `not-supported` (a job board we can identify but not read, e.g. Personio, Teamtailor, Dover) or `error`. Posts without any job-board link have no live fields.

**Does `onlyStillHiring` exclude posts that can't be checked?** Yes: it outputs only posts with a confirmed open role (`stillHiring: true`). Use `verifyOpenRoles` without `onlyStillHiring` to keep every post and just add the live fields.

**Can I get only remote jobs above a salary?** Yes: `remoteOnly: true` and `minSalary: 150000`. Posts without a published yearly salary are excluded when `minSalary` is set.

### Pricing

**$2 per 1,000 job posts** ($0.002 per result). No subscription. One month of "Who is hiring?" (~200–500 posts) costs about $0.40–$1. The live open-role check is included. Meta comments and posts already seen in monitor mode are free. HN jobs board ads cost the same as thread posts (job-page details included). Market trend rows (optional): **$0.01 per row** (one per month + one summary).

### Scheduling alerts

1. Set your filters, enable `onlyNewPosts` and give the monitor a `stateKey`.
2. Create a Schedule in Apify Console (e.g. daily at 9:00).
3. Connect Slack, email, Google Sheets, a webhook, Zapier or Make to receive new matching jobs automatically.

### Limits

Only public data is used: Hacker News, and for jobs board ads the public job page they link to (YC, Greenhouse, Ashby and Lever public APIs, or schema.org data on the page). The live open-role check reads public job-board lists only (titles, locations, dates). Hiring-manager and founder names are never output. Parsing is heuristic: posts that don't follow the usual `Company | Role | Location | ...` format may have some fields empty. The full `text` and `postUrl` are always included.

### More tools from the same developer

- [Company Jobs Scraper (ATS)](https://apify.com/signalscope-data/ats-jobs-scraper): every open job from Greenhouse, Lever, Ashby, Workable & more career sites, with new-job alerts.
- [Workday Jobs Scraper](https://apify.com/signalscope-data/workday-jobs-scraper): all jobs from any Workday career site, with salary and new-job monitoring.
- [Telegram Channel Scraper & Keyword Monitor](https://apify.com/signalscope-data/telegram-channel-monitor): posts, views and keyword alerts from public Telegram channels.

# Actor input Schema

## `months` (type: `integer`):

How many of the latest monthly threads to parse (1 = current month only). Ignored when 'Specific thread URLs' is filled.

## `threadUrls` (type: `array`):

Parse these threads instead, e.g. "https://news.ycombinator.com/item?id=49922569" or just "49922569". Use for older months or any HN thread whose top-level comments are job posts.

## `includeWhoIsHiring` (type: `boolean`):

Parse the monthly "Ask HN: Who is hiring?" threads (companies posting jobs).

## `includeWhoWantsToBeHired` (type: `boolean`):

Also parse "Ask HN: Who wants to be hired?" threads: location, remote, relocation and technologies of candidates. Emails, phone numbers, résumé and profile links are removed (no personal contact data is output).

## `includeFreelancer` (type: `boolean`):

Also parse the "Freelancer? Seeking freelancer?" threads (posted irregularly). Personal contact data is removed.

## `includeJobBoard` (type: `boolean`):

Also collect the job ads YC startups post on the Hacker News jobs board (news.ycombinator.com/jobs, e.g. "Acme (YC W24) Is Hiring a Founding Engineer"): company, YC batch, role, location, remote, apply link — plus salary, equity range, visa, minimum experience, skills, team size and founding year from the linked YC job page. Same filters and price as thread posts.

## `jobBoardOnly` (type: `boolean`):

Collect only HN jobs board ads and skip the monthly threads.

## `jobBoardDays` (type: `integer`):

How far back to collect jobs board ads (1-3650 days). About 40-60 ads are posted per month.

## `jobBoardDetails` (type: `boolean`):

Open each ad's link to add structured details: YC job pages (salary, equity, visa, experience, skills, company facts) or any page with schema.org JobPosting data (e.g. Greenhouse, Lever, company career pages). Also reports whether the listing is still open. Turn off for a faster run with title-only data.

## `outputFormat` (type: `string`):

'One row per company' merges all matching 'Who is hiring?' posts of the same company (matched by name and website) into a single lead row: post count, months posted, all roles, locations, tech stack, yearly salary range, apply links and — with hiring history — hiring status (new / returning / consistent). Each company row is billed as one result.

## `keywords` (type: `array`):

Output only posts containing ANY of these words or phrases (case-insensitive), e.g. "rust", "founding engineer", "fintech". Empty = all posts.

## `remoteOnly` (type: `boolean`):

Output only posts that offer remote work (including remote-or-onsite posts).

## `locations` (type: `array`):

Output only posts mentioning ANY of these locations in the location fields or headline, e.g. "NYC", "Berlin", "Europe", "Canada".

## `techs` (type: `array`):

Output only posts using ANY of these technologies, e.g. "Rust", "TypeScript", "Kubernetes", "PyTorch".

## `minSalary` (type: `integer`):

Output only posts with a parsed yearly salary whose upper bound is at least this amount (in the post's currency). Posts without a salary are excluded when set. 0 = no filter.

## `seniority` (type: `array`):

Output only job posts whose roles mention ANY of these seniority levels (detected from the role titles). Posts that don't state a level are excluded when set. Empty = all.

## `roleCategories` (type: `array`):

Output only job posts hiring for ANY of these role categories (classified from the role titles; one post can have several). Empty = all.

## `companyStages` (type: `array`):

Output only job posts whose text states ANY of these funding stages (e.g. "we just raised our Series A"). Posts that don't mention their stage are excluded when set. Empty = all.

## `equityOnly` (type: `boolean`):

Output only posts that mention equity, stock options, ESOP or RSUs.

## `includeTrends` (type: `boolean`):

Also output one 'market-trends' row per monthly 'Who is hiring?' thread: matching posts vs all posts, remote/onsite/hybrid, visa and equity share, salary p25/median/p75 per currency (yearly), top 30 technologies with rank and share change vs the previous month, role categories, seniority, top locations and funding stages — plus one 'market-trends-summary' row (rising and falling technologies) when 2+ months are covered. Your filters define the segment, e.g. Technologies = Rust gives the Rust job market. The previous month is fetched for comparison at no extra charge. Billed as one trend report per row.

## `trendsOnly` (type: `boolean`):

Output only the market trends rows (no job post / company rows are output or charged). Use with 'Number of most recent months' = 6–12 for a hiring-market report.

## `hiringHistoryMonths` (type: `integer`):

Enrich each job post with how often the company has posted in the last N monthly 'Who is hiring?' threads: companyMonthsHiring, companyHiringStreak (consecutive months), companyFirstSeenMonth, companyHiringMonths and isNewToHnHiring. Great for lead generation (who is hiring repeatedly vs. for the first time). 0 = off. Adds ~2–4 s per month of history; no extra charge.

## `onlyNewCompanies` (type: `boolean`):

Output only posts from companies that did not post in any earlier thread within the history window (uses 12 months if 'Company hiring history' is 0). First-time HN posters are often freshly funded startups.

## `verifyOpenRoles` (type: `boolean`):

For posts that link a public job board (Greenhouse, Lever, Ashby, Workable, SmartRecruiters, Recruitee or Workday), read that board now and add: stillHiring, liveOpenRoles, liveMatchingRoles (open roles in the same categories as the post), liveRemoteRoles, liveRolesSincePost, liveRolesLast30Days and up to 15 liveRoleTitles. One request per company, no extra charge. Every post always gets atsProvider / atsBoard / atsBoardUrl.

## `onlyStillHiring` (type: `boolean`):

Output only posts whose company has at least one open role on its job board right now (turns on the live check). Posts without a checkable job board link are excluded. Useful for older threads: skip companies that have stopped hiring.

## `includeNonJobComments` (type: `boolean`):

By default, meta comments (complaints, questions, 'thanks everyone') in hiring threads are skipped and not charged. Enable to output every top-level comment.

## `onlyNewPosts` (type: `boolean`):

Remember which posts were already seen and output only new ones. Ideal for a daily schedule: posts keep arriving all month and you never pay twice for the same post.

## `stateKey` (type: `string`):

Used with 'Only new posts'. Use a different name for each separate monitor/schedule (e.g. one per filter set).

## `maxItems` (type: `integer`):

Stop after this many results (0 = no limit). Useful to cap cost.

## Actor input object example

```json
{
  "months": 1,
  "includeWhoIsHiring": true,
  "includeWhoWantsToBeHired": false,
  "includeFreelancer": false,
  "includeJobBoard": false,
  "jobBoardOnly": false,
  "jobBoardDays": 30,
  "jobBoardDetails": true,
  "outputFormat": "posts",
  "remoteOnly": false,
  "equityOnly": false,
  "includeTrends": false,
  "trendsOnly": false,
  "hiringHistoryMonths": 0,
  "onlyNewCompanies": false,
  "verifyOpenRoles": false,
  "onlyStillHiring": false,
  "includeNonJobComments": false,
  "onlyNewPosts": false,
  "stateKey": "default",
  "maxItems": 0
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "months": 1
};

// Run the Actor and wait for it to finish
const run = await client.actor("signalscope-data/hn-hiring-parser").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "months": 1 }

# Run the Actor and wait for it to finish
run = client.actor("signalscope-data/hn-hiring-parser").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "months": 1
}' |
apify call signalscope-data/hn-hiring-parser --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,signalscope-data/hn-hiring-parser"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/39nXuw6T2Kzuc8Kf1/builds/fPht7L8RJPH2srMr3/openapi.json
