# HN Who wants to be hired — AI Structured Candidates (`arthur_taken/hn-who-wants-to-be-hired`) Actor

Developer candidates from Hacker News' monthly 'Who wants to be hired?' thread, parsed by AI: role, seniority, years, location, remote/relocation, visa, tech stack, email, résumé, GitHub.

- **URL**: https://apify.com/arthur\_taken/hn-who-wants-to-be-hired.md
- **Developed by:** [DH](https://apify.com/arthur_taken) (community)
- **Categories:** Jobs, Lead generation, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.50 / 1,000 candidate profiles

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 wants to be hired — AI Structured Candidates

Every month, hundreds of developers post in Hacker News' **"Ask HN: Who wants to be hired?"** thread: location, remote preference, tech stack, résumé and email. The posts are free text, so filtering by seniority, years of experience, visa needs or relocation is slow.

This Actor turns each post into a **clean, filterable candidate profile**, so you can find the right people in seconds.

### What you get

| Field | Example |
|---|---|
| `headline`, `category`, `seniority`, `years_experience` | Senior Backend Engineer · `software_engineering` · `senior` · 7 |
| `summary` | "Backend engineer with 7 years in Go and PostgreSQL, open to remote roles." |
| `city`, `country`, `timezone`, `location_text` | Lisbon · Portugal · UTC+1 |
| `work_mode_preference`, `willing_to_relocate` | `remote_only` / `remote_or_onsite` / `onsite_only` · `yes` / `no` |
| `needs_visa_sponsorship`, `work_authorization` | `no` · "EU citizen" |
| `employment_types` | `full_time`, `contract`, `freelance`, … |
| `tech_stack`, `technologies_text` | Go, PostgreSQL, Kubernetes (aliases unified: Postgres → PostgreSQL) |
| `emails`, `resume_url`, `github_url`, `linkedin_url`, `website_url` | copied verbatim from the post |
| `hn_username`, `hn_url`, `posted_at`, `month` | link back to the original post |
| `first_seen_month`, `months_posted`, `is_repeat_poster` | "still looking" signal across months |

How the data is built:

- **Contacts and links are never AI-generated.** Emails, résumé, GitHub and LinkedIn links are copied from the post text by exact rules. Obfuscated emails (`name [at] domain`) are not decoded.
- **AI (Claude) reads the whole post** for headline, seniority, years, location, work preferences, visa status and tech stack — including posts that don't follow the usual template.
- **No guessing.** If the post doesn't say it, the field is `null` or `"unspecified"`.
- **One row per person** by default: when you search several months, each HN user appears once, from their latest post.

### Input

| Option | Description |
|---|---|
| `months` | `latest`, `all`, or `YYYY-MM` (e.g. `2026-09`) |
| `keywords` | any of, in headline, summary and technologies (e.g. `data engineer`, `devops`) |
| `techStack` | any of, exact name with aliases unified (e.g. `Go`, `PostgreSQL`) |
| `locations` | any of, e.g. `Germany`, `Berlin`, `Europe`, `UTC+1` |
| `workModes` | `remote_only`, `remote_or_onsite`, `onsite_only` |
| `seniority`, `minYearsExperience`, `maxYearsExperience` | experience filters |
| `categories`, `employmentTypes` | e.g. `data`, `ml_ai` · `contract`, `freelance` |
| `willingToRelocate`, `excludeNeedsSponsorship` | relocation and visa filters |
| `requireEmail`, `requireResume` | only people you can contact right away |
| `excludeRepeatPosters`, `postedAfter` | freshness filters |
| `onePerPerson` | one row per HN user (default on) |
| `onlyNew` | for scheduled runs: only candidates you haven't received yet |
| `maxItems` | cap the number of rows (and your cost) |

Example — senior Go/PostgreSQL engineers in Europe who can be contacted by email:

```json
{
  "months": ["all"],
  "techStack": ["Go", "PostgreSQL"],
  "locations": ["Europe", "Germany", "Poland", "Portugal", "Spain", "Netherlands"],
  "seniority": ["senior", "staff", "principal"],
  "requireEmail": true,
  "maxItems": 100
}
```

### Weekly candidate alerts

Schedule the Actor with `onlyNew: true` and your filters to receive only new people each time. You pay only for new rows.

### Using it from an AI agent (MCP)

Works as a tool through the [Apify MCP server](https://mcp.apify.com). Minimal call: `{"months": ["latest"], "techStack": ["Python"], "maxItems": 50}`. Every row has the same fields; if nothing matches, the status message says so and lists the months that have data.

### Coverage and freshness

- **Months covered:** every monthly thread from **April 2026** onward, and each new month is added automatically.
- New threads are processed within a day of posting; late posts are added daily.
- About **500 candidates per month**.

### Pricing

**$2.50 per 1,000 candidates.** You pay only for rows that match your filters. Use `maxItems` to cap spend. Runs take a few seconds because posts are parsed in advance.

### Data source and responsible use

- Data comes from the official Hacker News API: public posts people wrote to be contacted about jobs.
- Use contact details **only to contact people about job opportunities**. No bulk marketing, no reselling contact lists. You are responsible for complying with laws such as GDPR and CAN-SPAM.
- AI extraction is accurate but not perfect — check the original post (`hn_url`) before acting.

### Related

- [HN Who is Hiring — AI Structured Jobs](https://apify.com/arthur_taken/hn-who-is-hiring-ai): the companies' side — 12+ months of job posts with salary, remote regions, visa and tech stack.

# Actor input Schema

## `months` (type: `array`):

Which monthly threads to search: 'latest' (newest month with data), 'all' (every covered month), or YYYY-MM such as '2026-09'. With onePerPerson, each person appears once, from their most recent post.

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

Free-text filter over headline, summary and technologies (case-insensitive, matched at word start: 'engineer' matches 'Engineering', 'ML' does not match 'HTML'), e.g. 'data engineer', 'devops', 'founding'.

## `techStack` (type: `array`):

Keep people whose tech\_stack contains at least one of these. Exact name match, case-insensitive, aliases unified ('Postgres' = 'PostgreSQL', 'NextJS' = 'Next.js', 'Golang' = 'Go'). For looser search use keywords.

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

Matched at word start in the location line, city, country, time zone and remote line, e.g. 'Germany', 'Berlin', 'Europe', 'UTC+1', 'US'. The location line is free text, so add city or country names you care about.

## `workModes` (type: `array`):

remote\_only = wants remote only; remote\_or\_onsite = also open to office, hybrid or relocation; onsite\_only = office only. Empty = all.

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

From an explicit title or stated years; many posts don't say, so include 'unspecified' to avoid missing people.

## `minYearsExperience` (type: `integer`):

Keep people who state at least this many years. People who don't state years (about half) are dropped when this is set.

## `maxYearsExperience` (type: `integer`):

Keep people who state at most this many years (useful for junior roles). People who don't state years are dropped when this is set.

## `categories` (type: `array`):

Job family inferred from the post, e.g. 'data', 'ml\_ai', 'devops\_infra'.

## `employmentTypes` (type: `array`):

Keep people open to at least one of these contract types (when stated).

## `willingToRelocate` (type: `boolean`):

Keep only people who explicitly say they are willing to relocate.

## `excludeNeedsSponsorship` (type: `boolean`):

Drop people who say they need visa sponsorship. People who don't mention visas are kept.

## `requireEmail` (type: `boolean`):

Keep only posts with an email written in plain text (about 70%). Obfuscated addresses are not decoded.

## `requireResume` (type: `boolean`):

Keep only posts with a résumé/CV link.

## `excludeRepeatPosters` (type: `boolean`):

Drop people who also posted in an earlier covered month. Keep them (default) if you want people still actively looking.

## `postedAfter` (type: `string`):

Keep posts published on or after this date: '2026-09-15' or relative like '7 days'. Applies within the selected months.

## `onePerPerson` (type: `boolean`):

When several months are selected, return each HN user once, from their most recent post. Turn off to get every post.

## `onlyNew` (type: `boolean`):

For scheduled runs: return only candidates you haven't received from this Actor with the same filters. IDs are kept in a key-value store named 'hn-who-wants-to-be-hired-state' in your account. With 'latest', the last 2 months are checked.

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

Maximum number of candidates to return. Each row is billed, so this caps your cost. Empty = all matching (about 500 per month).

## Actor input object example

```json
{
  "months": [
    "all"
  ],
  "keywords": [
    "backend",
    "platform"
  ],
  "techStack": [
    "Go",
    "PostgreSQL"
  ],
  "locations": [
    "Germany",
    "Poland",
    "Europe"
  ],
  "workModes": [
    "remote_or_onsite"
  ],
  "seniority": [
    "senior",
    "staff",
    "unspecified"
  ],
  "minYearsExperience": 5,
  "maxYearsExperience": 3,
  "categories": [
    "data",
    "ml_ai"
  ],
  "employmentTypes": [
    "contract",
    "freelance"
  ],
  "willingToRelocate": false,
  "excludeNeedsSponsorship": false,
  "requireEmail": false,
  "requireResume": false,
  "excludeRepeatPosters": false,
  "postedAfter": "14 days",
  "onePerPerson": true,
  "onlyNew": false,
  "maxItems": 100
}
```

# Actor output Schema

## `candidates` (type: `string`):

All matching candidate rows (headline, seniority, location, work preferences, tech stack, contact links).

# 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": [
        "latest"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("arthur_taken/hn-who-wants-to-be-hired").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": ["latest"] }

# Run the Actor and wait for it to finish
run = client.actor("arthur_taken/hn-who-wants-to-be-hired").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": [
    "latest"
  ]
}' |
apify call arthur_taken/hn-who-wants-to-be-hired --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,arthur_taken/hn-who-wants-to-be-hired"
        }
    }
}
```

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/n7qi1FOavxOLva9TG/builds/fIOeVcKaETYju5ah6/openapi.json
