# Resume Scraper & Candidate Sourcing - Hacker News (`angaba92/hacker-news-who-wants-to-be-hired-scraper`) Actor

Resume scraper and candidate sourcing for recruiting teams: parses the monthly Hacker News Who wants to be hired thread into developer profiles with email, resume URL, GitHub, tech stack, seniority and remote preference. Candidates actively looking. Export CSV, JSON or API.

- **URL**: https://apify.com/angaba92/hacker-news-who-wants-to-be-hired-scraper.md
- **Developed by:** [Andres Garcia-Baquero Leon](https://apify.com/angaba92) (community)
- **Categories:** Jobs, Lead generation, Developer tools
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Hacker News Who Wants to Be Hired Scraper - Candidates

Get a sourcing-ready dataset of software engineers who publicly said they want a new job this month. This Actor turns the monthly **"Ask HN: Who wants to be hired?"** thread into structured candidate profiles with location, tech stack, remote and relocation preferences, resume link, GitHub, LinkedIn and a contact email.

You do not get a pile of HTML comments. You get rows you can filter, score and push straight into your ATS, CRM or sourcing spreadsheet.

### Job scrapers vs this Actor

Everyone scrapes the "Who is hiring?" side. That is the demand side, and it is crowded. This Actor covers the **supply side**: the candidates themselves.

| A job scraper gives you | This Actor gives you |
|---|---|
| Open roles and company job ads | Real people actively looking for work |
| Companies you then have to pitch | Candidates who asked to be contacted |
| Recruiter competition on every posting | Uncontested talent most sourcers never touch |
| Company name, title, description | Location, stack, seniority, email, resume, GitHub, LinkedIn |
| Leads you must qualify for intent | Stated intent to be hired, in their own words |
| Something you sell into | Something you place, which is the billable side |

### Example output

```json
{
  "location": "Geneva, Switzerland",
  "remote": true,
  "willingToRelocate": true,
  "technologies": ["C++", "Python", "CUDA", "Go", "PyTorch", "LLM inference", "RAG", "Docker"],
  "seniority": "unspecified",
  "yearsExperience": null,
  "email": "mail@elashri.com",
  "resumeUrl": "https://melashri.net/pdf/CV.pdf",
  "githubUrl": null,
  "linkedinUrl": null,
  "contactScore": 5,
  "hasContact": true,
  "hnUrl": "https://news.ycombinator.com/item?id=49183126",
  "hnUser": "elashri",
  "threadTitle": "Ask HN: Who wants to be hired? (August 2026)"
}
```

Flattened into a table, the same record looks like this:

| hnUser | location | remote | willingToRelocate | seniority | technologies | email | resumeUrl | contactScore |
|---|---|---|---|---|---|---|---|---|
| elashri | Geneva, Switzerland | true | true | unspecified | C++, Python, CUDA, Go, PyTorch, LLM inference, RAG, Docker | mail@elashri.com | https://melashri.net/pdf/CV.pdf | 5 |

### Output fields

| Field | Type | Description |
|---|---|---|
| `location` | string | Stated location (city, country, timezone) |
| `remote` | boolean / null | Parsed from the `Remote:` line |
| `willingToRelocate` | boolean / null | Parsed from the `Willing to relocate:` line |
| `technologies` | array | Clean array of technologies, for example `["Rust", "Kubernetes", "Postgres"]` |
| `seniority` | string | `principal`, `senior`, `mid`, `junior`, or `unspecified` |
| `yearsExperience` | integer / null | Years of experience when stated |
| `email` | string / null | Contact email, including de-obfuscated `name at domain dot com` forms |
| `resumeUrl` | string / null | Resume or CV link, full URL, un-truncated |
| `githubUrl` | string / null | GitHub profile |
| `linkedinUrl` | string / null | LinkedIn profile |
| `otherLinks` | array | Personal site, portfolio, blog and similar links |
| `contactScore` | integer | 0-7 how actionable the lead is (email=3, resume=2, GitHub=1, LinkedIn=1) |
| `hasContact` | boolean | Whether any contact channel was found |
| `hnUrl` | string | Direct link to the HN comment |
| `hnUser` | string | HN username |
| `postedAt` | integer | Unix timestamp |
| `threadTitle` | string | For example `Ask HN: Who wants to be hired? (August 2026)` |
| `text` | string | Full plain-text profile |

### How to use it

1. Click **Try for free** on the Actor page in Apify Store.
2. Leave the input empty. Running with **empty input `{}`** works out of the box: it parses the latest "Who wants to be hired?" thread with no filters. Add filters only when you want a narrower list.
3. Click **Start** and watch candidates stream into the dataset while the run is still going.
4. Export the results as **CSV, JSON, Excel, XML or HTML**, pull them through the **Apify API**, or fire a **webhook** into your own system.

### Input

```json
{
  "threadType": "wants_to_be_hired",
  "month": "latest",
  "remoteOnly": true,
  "mustHaveEmail": true,
  "requiredTech": ["rust", "kubernetes"],
  "maxResults": 500
}
```

| Field | Type | Default | Description |
|---|---|---:|---|
| `threadType` | string | `wants_to_be_hired` | `wants_to_be_hired` (job seekers) or `freelancer` (Freelancer? Seeking freelancer? thread) |
| `month` | string | `latest` | `latest` or a specific month like `July 2026` |
| `threadId` | string | - | Override: parse a specific HN story item ID |
| `remoteOnly` | boolean | `false` | Only candidates open to remote work |
| `willingToRelocateOnly` | boolean | `false` | Only candidates willing to relocate |
| `mustHaveEmail` | boolean | `false` | Only candidates with a detected email |
| `requiredTech` | array | `[]` | Match ANY of these technologies |
| `keywords` | array | `[]` | Match ANY keyword anywhere in the profile |
| `maxResults` | integer | `500` | Max profiles to return (1-1000) |

#### Example 1: remote senior Rust candidates

```json
{
  "threadType": "wants_to_be_hired",
  "month": "latest",
  "remoteOnly": true,
  "requiredTech": ["rust"],
  "keywords": ["senior"],
  "maxResults": 200
}
```

#### Example 2: only candidates with a resume and an email

```json
{
  "threadType": "wants_to_be_hired",
  "month": "July 2026",
  "mustHaveEmail": true,
  "keywords": ["resume", "cv"],
  "maxResults": 500
}
```

Then sort the dataset by `contactScore` and work the top of the list first.

### Use cases

- **Technical recruiters** - build a monthly shortlist of inbound, remote-ready engineers before your competitors read the thread.
- **Recruiting and staffing agencies** - refill the bench every month with candidates who have stated hiring intent.
- **Talent pipelines** - accumulate months of profiles into one searchable pool segmented by stack, seniority and location.
- **Headhunting** - filter for `principal` and `senior` seniority plus a specific stack, and go direct with an email or resume in hand.
- **Talent market and salary research** - track which stacks, locations and remote preferences dominate month over month.
- **ATS and CRM enrichment** - match parsed GitHub, LinkedIn and resume URLs onto records you already hold.
- **Freelance vendor discovery** - switch `threadType` to `freelancer` to source contractors instead of full-time hires.

### Integrations

Connect the output to whatever you already run:

- **Zapier**, **Make** and **n8n** for no-code automations.
- **Slack** to drop new matching candidates into a sourcing channel.
- **Google Sheets** and **Airtable** for shared shortlists.
- **Airbyte** to load candidates into your warehouse.
- **LangChain** and the Apify integrations for AI screening and ranking.
- **REST API** and **webhooks** to trigger runs and receive results in your own stack.

Set a **monthly schedule** in Apify so the Actor fires shortly after each new thread goes up, and every month's candidates land in your pipeline without anyone remembering to press a button.

### Field coverage

Measured on the live August 2026 thread (54 parsed profiles from the first 60 comments):

| Field | Coverage |
|---|---|
| `location` | 98% |
| `technologies` | 92% |
| `remote` | 85% |
| `willingToRelocate` | 79% |
| `resumeUrl` | 79% |
| `email` | 70% |
| `hasContact` (any channel) | 94% |

Coverage depends on what each candidate chose to include in their post.

### Pricing

Pay per result - you only pay for candidate profiles actually returned.

| Event | Price |
|---|---|
| Candidate profile | **$0.02** |
| Actor start | $0.00005 |

A full month's thread (~400 candidates) costs about **$8** - cheaper than a single sourcing-tool seat, for leads with stated intent to be hired. Use `maxResults` and the filters to control spend.

### Reliability

- 100% **official APIs** (Hacker News Firebase API + Algolia HN Search for thread discovery) - no HTML scraping, no proxies, no login, no anti-bot risk.
- Retries with exponential backoff on rate limits and 5xx responses.
- Incremental pushing - a late failure still leaves you the results already parsed.
- A single malformed comment can never fail the run.

### FAQ

**Do I need a Hacker News account or API key?**
No. There is no login, no cookie and no proxy configuration. The Actor reads the official public Hacker News Firebase API and uses Algolia HN Search to find the right monthly thread.

**Is this legal?**
The data is public. Candidates post these profiles themselves, in a thread whose explicit purpose is to be contacted by employers. Use them for genuine recruiting outreach, honour opt-outs, and comply with GDPR, CAN-SPAM and applicable local law. Do not use this dataset for bulk unsolicited marketing unrelated to hiring.

**How fresh is the data?**
The "Ask HN: Who wants to be hired?" thread is posted once a month. With `month: "latest"` the Actor always targets the current thread, and candidates keep posting to it throughout the month, so re-running gives you newly added profiles.

**Why are some fields empty or null?**
Because the candidate did not include them. Nothing is guessed or filled in. If someone omits their email or does not write a `Remote:` line, those fields come back `null`. See the field coverage table above for what to expect.

**Can I get past months?**
Yes. Set `month` to a specific month like `"July 2026"`, or pass a `threadId` to parse a specific HN story item ID directly. Run it once per month to backfill a full pipeline.

**Will it break?**
It runs entirely on official Hacker News APIs rather than HTML scraping, so there is no markup to break and no anti-bot system to trip. Rate limits and 5xx responses are retried with exponential backoff, and results are pushed incrementally so a late error never costs you the profiles already parsed.

**Can I get freelancers instead of full-time job seekers?**
Yes. Set `threadType` to `freelancer` to parse the "Freelancer? Seeking freelancer?" thread instead.

### Troubleshooting

| Problem | Fix |
|---|---|
| Zero results returned | Your filters are too strict. Remove `requiredTech` / `keywords` or turn off `remoteOnly` and `mustHaveEmail`, then re-run. |
| Fewer results than expected | Raise `maxResults` (max 1000). Early in the month the thread simply has fewer comments. |
| Wrong month parsed | Set `month` explicitly, for example `"July 2026"`, or pass the exact `threadId`. |
| `email` is null for many rows | Those candidates did not post an email. Filter with `mustHaveEmail: true`, or use `resumeUrl` and `githubUrl` instead. |
| Technologies look incomplete | The parser only extracts what the candidate wrote. Use the `text` field for the full raw profile. |
| Run cost higher than expected | Pricing is per candidate profile returned. Lower `maxResults` and tighten filters. |
| Run slower than expected on big threads | Large threads mean more API calls. Results are pushed incrementally, so you can start reading the dataset before the run finishes. |
| Need results in another system | Export to CSV, JSON, Excel, XML or HTML, or wire up a webhook or the REST API integration. |

### Responsible use

These profiles are published voluntarily and publicly by the candidates themselves in a thread whose explicit purpose is to be contacted by employers. Even so, they contain personal data. Use them for genuine recruiting outreach, honour opt-outs, and comply with GDPR, CAN-SPAM and any applicable local law.

**Store categories:** JOBS, LEAD\_GENERATION, DEVELOPER\_TOOLS

# Actor input Schema

## `threadType` (type: `string`):

Which monthly HN thread to parse. 'wants\_to\_be\_hired' = job seekers. 'freelancer' = the 'Freelancer? Seeking freelancer?' thread.

## `month` (type: `string`):

Which monthly thread to parse. Use 'latest' for the newest thread, or a specific month like 'July 2026'.

## `threadId` (type: `string`):

Override: parse a specific HN story item ID. Takes precedence over Thread type and Month.

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

Only return candidates who explicitly stated they are open to remote work.

## `willingToRelocateOnly` (type: `boolean`):

Only return candidates who explicitly stated they are willing to relocate.

## `mustHaveEmail` (type: `boolean`):

Only return candidates with a detected email address (including de-obfuscated 'name at domain dot com').

## `requiredTech` (type: `array`):

Only return candidates whose parsed tech stack matches ANY of these (case-insensitive), e.g. rust, kubernetes, react.

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

Only return candidates whose full profile text matches ANY of these keywords (case-insensitive), e.g. berlin, security, staff.

## `maxResults` (type: `integer`):

Maximum number of candidate profiles to return (1-1000).

## Actor input object example

```json
{
  "threadType": "wants_to_be_hired",
  "month": "latest",
  "remoteOnly": false,
  "willingToRelocateOnly": false,
  "mustHaveEmail": false,
  "requiredTech": [],
  "keywords": [],
  "maxResults": 500
}
```

# 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 = {
    "threadType": "wants_to_be_hired",
    "month": "latest",
    "remoteOnly": false,
    "willingToRelocateOnly": false,
    "mustHaveEmail": false,
    "requiredTech": [],
    "keywords": [],
    "maxResults": 500
};

// Run the Actor and wait for it to finish
const run = await client.actor("angaba92/hacker-news-who-wants-to-be-hired-scraper").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 = {
    "threadType": "wants_to_be_hired",
    "month": "latest",
    "remoteOnly": False,
    "willingToRelocateOnly": False,
    "mustHaveEmail": False,
    "requiredTech": [],
    "keywords": [],
    "maxResults": 500,
}

# Run the Actor and wait for it to finish
run = client.actor("angaba92/hacker-news-who-wants-to-be-hired-scraper").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 '{
  "threadType": "wants_to_be_hired",
  "month": "latest",
  "remoteOnly": false,
  "willingToRelocateOnly": false,
  "mustHaveEmail": false,
  "requiredTech": [],
  "keywords": [],
  "maxResults": 500
}' |
apify call angaba92/hacker-news-who-wants-to-be-hired-scraper --silent --output-dataset

```

## MCP server setup

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

```

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/MH5K7cDTvDXVsDr5k/builds/GZ9SzeqRKE7dQ6ki3/openapi.json
