# HN Who Is Hiring: Structured Jobs (`yzy001/hn-hiring-jobs`) Actor

Turns the monthly Hacker News 'Who is hiring?' thread into structured rows: company, remote/hybrid/on-site, visa sponsorship, salary range with currency, employment type and tech tags. Field accuracy measured on hand-labelled posts and stated.

- **URL**: https://apify.com/yzy001/hn-hiring-jobs.md
- **Developed by:** [宗諭 楊](https://apify.com/yzy001) (community)
- **Categories:** Jobs, Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.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

## HN Who Is Hiring: Structured Jobs

Every month Hacker News runs an "Ask HN: Who is hiring?" thread with a few hundred job posts, each written as free text. This Actor reads the thread and turns every post into a structured row you can filter, sort and load into a spreadsheet or database.

### What you get for each job post

| Field | Meaning |
|---|---|
| `company` | Company name as written in the post. Empty when the post never names one |
| `workplace` | `REMOTE` (remote only), `HYBRID` (some days in an office), `ONSITE` (on-site only), `MIXED` (remote offered as an alternative to an office) or `UNKNOWN` (not stated) |
| `remoteAllowed` | `true` when at least one role can be done remotely, `false` when the post says on-site or hybrid, empty when not stated |
| `visaSponsorship` | `true` when the post says it sponsors visas, `false` when it says it cannot or requires citizenship, empty when it does not mention visas |
| `salary` | `min`, `max`, `currency`, `period` (`YEAR` or `HOUR`) and the matching `text`. The lowest and highest pay stated anywhere in the post, across all roles |
| `employmentTypes` | `FULL_TIME`, `PART_TIME`, `CONTRACT`, `INTERNSHIP` when mentioned |
| `equityMentioned` | whether the post mentions equity, stock options or RSUs |
| `techTags` | technologies named in the post (Python, TypeScript, Rust, Kubernetes, PostgreSQL, LLM and about 50 more) |
| `monthsPostedInRun` | when you read several months, how many of them this company posted in |
| `header`, `text`, `urls` | the post's first line, its full text and its links |
| `month`, `postedAt`, `postUrl`, `author` | where and when it was posted |

### How accurate it is

Every field is read from free text by rules, so it was measured, not assumed. 70 posts from the July and August 2026 threads, from companies not seen while the rules were written, were labelled by hand before the Actor was run on them:

| Field | Correct |
|---|---|
| Is it a job post at all | 98.6% |
| company | 100% |
| workplace | 88.9% |
| remoteAllowed | 100% |
| visaSponsorship | 96.8% |
| salary (min, max, currency, period) | 98.4% |

These are the first-run numbers. The mistakes they exposed (for example "On-site 4 days/week" read as on-site instead of hybrid, and "No standard visa sponsorship" read as yes) have since been fixed. The same figures are in each run's summary.

### Filters

| Input | What it does |
|---|---|
| `monthsBack` | How many monthly threads to read, newest first (1 to 12) |
| `keywords` | Keep posts whose text contains any of these words |
| `techs` | Keep posts tagged with any of these technologies |
| `workplace` | Keep only some workplace types |
| `remoteAllowedOnly` | Keep only posts where a role can be done remotely |
| `visaSponsorshipOnly` | Keep only posts that say they sponsor visas |
| `minAnnualSalary` + `salaryCurrency` | Keep only posts whose stated annual pay reaches the floor in that currency |
| `includeText` | Leave out the full text for smaller rows |
| `maxItems` | Cap on rows |

### Run summary

Stored under the key `SUMMARY` in the key-value store: posts per month and how many were left out as non-job posts, the workplace mix, how many posts mention visas, how many state a salary, the median salary midpoint per currency, the most common tech tags, companies that posted in every month you read, the measured accuracy and the caveats below.

### Things to know before you rely on it

- **Nothing is guessed.** When a post does not say something, the field is empty. Most posts do not mention visas, so `visaSponsorship` is usually empty.
- **Salary is what the post states.** Funding rounds, market sizes, revenue and 401k are not pay. Hourly rates are marked `HOUR`. A range written without a currency is kept with `currency` empty. Nothing is converted between currencies.
- **Region limits stay in the text.** "Remote (US only)" is `REMOTE`; the US limit is in `header` and `text`.
- **Not every top-level comment is a job post.** Comments about the thread and job seekers posting in the wrong thread are left out and counted in the summary.
- **No contact list is extracted.** Emails stay inside the post text exactly as the poster wrote them.

### Data

The Actor reads Hacker News through its public search API, the same data anyone can browse on the site. It does not log in, does not use cookies and does not work around any blocking. A run reads one request per month plus one to find the threads, so it finishes in seconds.

# Actor input Schema

## `monthsBack` (type: `integer`):

How many monthly 'Who is hiring?' threads to read, newest first. 1 is the current month.

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

Optional. Keep only posts whose text contains at least one of these words, case-insensitive. Example: Rust, robotics, Berlin.

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

Optional. Keep only posts tagged with at least one of these technologies (see techTags in the output), for example Python, TypeScript, Kubernetes.

## `workplace` (type: `array`):

Optional. Keep only these workplace types. Leave empty for all.

## `remoteAllowedOnly` (type: `boolean`):

Keep only posts where at least one role can be done remotely (workplace REMOTE or MIXED).

## `visaSponsorshipOnly` (type: `boolean`):

Keep only posts that say they sponsor visas. Most posts do not mention visas at all, so this keeps few rows.

## `minAnnualSalary` (type: `integer`):

Optional. Keep only posts whose stated annual pay reaches at least this amount in the currency below. Posts without a stated annual salary in that currency are left out when this is set. 0 = no filter.

## `salaryCurrency` (type: `string`):

Currency for the minimum salary filter. Nothing is converted between currencies.

## `includeText` (type: `boolean`):

Include the whole post as written. Turn off for smaller rows.

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

Upper bound on rows returned after filtering.

## Actor input object example

```json
{
  "monthsBack": 1,
  "keywords": [],
  "techs": [],
  "workplace": [],
  "remoteAllowedOnly": false,
  "visaSponsorshipOnly": false,
  "minAnnualSalary": 0,
  "salaryCurrency": "USD",
  "includeText": true,
  "maxItems": 1000
}
```

# Actor output Schema

## `items` (type: `string`):

Structured job posts, newest first.

## `summary` (type: `string`):

Per-month counts, workplace and salary statistics, top tech tags, measured accuracy and caveats.

# 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 = {
    "monthsBack": 1,
    "maxItems": 1000
};

// Run the Actor and wait for it to finish
const run = await client.actor("yzy001/hn-hiring-jobs").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 = {
    "monthsBack": 1,
    "maxItems": 1000,
}

# Run the Actor and wait for it to finish
run = client.actor("yzy001/hn-hiring-jobs").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 '{
  "monthsBack": 1,
  "maxItems": 1000
}' |
apify call yzy001/hn-hiring-jobs --silent --output-dataset

```

## MCP server setup

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

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/ztXtnnFFggbHeiATt/builds/fo16ICqFyhPxQa9q1/openapi.json
