# Hacker News Scraper API: Stories + Who Is Hiring Jobs (`fecklesspleb/hacker-news-scraper`) Actor

Search Hacker News stories and comments, or turn the monthly "Who is hiring?" thread into structured jobs. Only-new-items mode. $0.15 per 1,000 items.

- **URL**: https://apify.com/fecklesspleb/hacker-news-scraper.md
- **Developed by:** [Scott Williams](https://apify.com/fecklesspleb) (community)
- **Categories:** News, Jobs, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.15 / 1,000 items

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 Scraper API: search stories and comments, and parse "Who is hiring?" jobs

Hacker News Scraper has two modes. **Search** returns Hacker News stories, comments, Ask HN, Show HN, jobs and polls that match a query, with points, comment counts, dates and links, using the public HN Search API by Algolia. **Who is hiring** reads the monthly "Ask HN: Who is hiring?" threads and turns each top-level post into a structured job: company, role, location, remote/onsite flags, salary when stated, and links. Emails and phone numbers are redacted by default. You pay $0.00015 per item written to the dataset.

### Who it's for

- **Founders, marketers and researchers** tracking what Hacker News says about a product, company or topic.
- **Job seekers and recruiters** filtering the monthly "Who is hiring?" thread by stack, remote and salary.
- **Developers** who need HN data as JSON without paging through the API themselves.
- **Newsletter and content teams** collecting top stories on a topic each week.

### What data you get

**Search mode**, one item per story or comment:

| Field | What it holds |
|---|---|
| `id`, `type` | HN item ID and type (story, comment, poll, job…). |
| `title`, `url`, `domain` | Story title and link (null for comments and text posts). |
| `text` | Post or comment text, as plain text. |
| `points`, `numComments` | Score and comment count. |
| `createdAt` | ISO 8601 timestamp. |
| `storyId`, `parentId` | For comments: the story and parent item. |
| `isFrontPage` | Whether Algolia tags the story as front page. |
| `hnUrl`, `scrapedAt` | Link to the item on news.ycombinator.com, and when it was read. |

The author's username is not included.

```json
{
  "id": "50281851",
  "type": "story",
  "title": "Our AI Agent Executed the Same Successful SQL Query 254 Times",
  "url": "https://armincat.com/articles/ai-agent-executed-same-query-254-times",
  "domain": "armincat.com",
  "text": null,
  "points": 3,
  "numComments": 0,
  "createdAt": "2026-10-28T22:29:54Z",
  "storyId": 50281851,
  "parentId": null,
  "isFrontPage": false,
  "hnUrl": "https://news.ycombinator.com/item?id=50281851",
  "scrapedAt": "2026-10-29T01:01:18.594163+00:00"
}
```

**Who is hiring mode**, one item per job post:

| Field | What it holds |
|---|---|
| `id` | HN comment ID. |
| `company`, `role`, `location` | Parsed from the post's first line (split on `\|`). Null when the post doesn't follow the usual format. |
| `isRemote`, `isOnsite` | From the first line. |
| `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryPeriod` | When a range is stated. |
| `headline`, `text` | First line and full post text (contacts redacted by default). |
| `links` | Up to 10 links from the post (email links excluded). |
| `postedAt`, `threadTitle`, `threadId`, `hnUrl`, `scrapedAt` | Post date, thread and links. |

```json
{
  "id": "50095921",
  "company": "NeuralFrame",
  "role": "Senior Backend Engineer, Medical Data Pipeline (Python + Rust)",
  "location": "REMOTE (US only, US time zones; no visa sponsorship)",
  "isRemote": true,
  "isOnsite": false,
  "salaryMin": 170000,
  "salaryMax": 210000,
  "salaryCurrency": "USD",
  "salaryPeriod": "YEAR",
  "headline": "NeuralFrame | Senior Backend Engineer, Medical Data Pipeline (Python + Rust) | REMOTE (US only, US time zones; no visa sponsorship) | Full-time | $170k–$210k + 0.1%–0.25% equity",
  "text": "NeuralFrame | Senior Backend Engineer, Medical Data Pipeline (Python + Rust) | REMOTE (US only, US time zones; no visa sponsorship) | Full-time | $170k–$210k + 0.1%–0.25% equity\nWe build software that hospitals, cancer r…",
  "links": [
    "https://wellfound.com/jobs/4829721-senior-backend-software-e"
  ],
  "postedAt": "2026-10-14T21:53:28.000Z",
  "threadTitle": "Ask HN: Who is hiring? (October 2026)",
  "threadId": "49922569",
  "hnUrl": "https://news.ycombinator.com/item?id=50095921",
  "scrapedAt": "2026-10-29T01:01:18.596278+00:00"
}
```

### How to use it

| Input field | Label | Notes |
|---|---|---|
| `mode` | Mode | `search` (default) or `whoIsHiring`. |
| `query` | Search query | Search mode. Empty lists the newest items. |
| `tags` | Item types | Search mode. `story`, `comment`, `ask_hn`, `show_hn`, `job`, `poll`, `front_page`. Default `story`. See the note below. |
| `sortBy` | Sort by | `date` (newest first, default) or `relevance`. |
| `minPoints` | Min points | Search mode. |
| `minComments` | Min comments | Search mode. |
| `postedWithinDays` | Posted within (days) | Search mode. |
| `since`, `until` | From date / To date | Search mode, `YYYY-MM-DD`. |
| `threads` | Who is hiring: number of monthly threads | Default 1 (latest month). |
| `keywords` | Who is hiring: keywords (any) | Matches anywhere in the post text. |
| `remoteOnly` | Who is hiring: remote only | Keeps posts that say remote in the first line. |
| `maxItems` | Max results | Default 100. |
| `redactContactDetails` | Redact emails and phone numbers | Default `true`. |
| `onlyNewItems` | Only new items (monitoring) | Default `false`. Seen items are remembered for 120 days. |
| `stateStoreName` | State store name | Default `hacker-news-scraper-state`. |

**How `tags` combine:** separate entries are combined with AND by the Algolia API, so `["story", "front_page"]` means front-page stories. To get either of several types, put them in one entry separated by commas: `["story,comment"]` means stories OR comments.

Example: AI agent stories with 50+ points from the last 30 days.

```json
{
  "mode": "search",
  "query": "AI agents",
  "tags": [
    "story"
  ],
  "minPoints": 50,
  "postedWithinDays": 30,
  "maxItems": 200
}
```

Example: this month's remote Python or Rust jobs from "Who is hiring?".

```json
{
  "mode": "whoIsHiring",
  "threads": 1,
  "keywords": [
    "python",
    "rust"
  ],
  "remoteOnly": true,
  "maxItems": 500
}
```

With `sortBy: "date"`, the Actor pages past Algolia's 1,000-results-per-query limit by moving the time window, so long date ranges work. With `relevance`, results stop at 1,000.

### Pricing

This Actor uses pay-per-event pricing: **$0.00015 per item** written to the dataset. That is **$0.15 per 1,000 items**. There is no separate start fee in this Actor's pricing (as listed on October 28, 2026; the Pricing tab on the Store page is the source of truth).

You are charged only for items that end up in your dataset. Items removed by your filters and items skipped in monitoring mode are not charged.

| Example | Items | Cost |
|---|---:|---:|
| One "Who is hiring?" thread (illustration) | 400 | $0.06 |
| Weekly topic search, 200 stories | 200 | $0.03 |
| Large historical comment pull | 100,000 | $15.00 |

Rows are illustrations; thread sizes vary month to month.

Worked example: 1,000 items × $0.00015 = **$0.15**.

To cap spend, set **Maximum cost per run** in the run options (or `maxTotalChargeUsd` via the API). The Actor stops cleanly when the next item would go over the limit and records `stoppedByCostLimit: true` in the run summary.

### How it compares

Prices are from the public Apify Store listings on October 28, 2026.

| Option | Pricing | What it is good at |
|---|---|---|
| This Actor | $0.00015 per item | Search with filters and date paging, plus parsed "Who is hiring?" jobs with contacts redacted, and only-new-items mode. |
| Other Hacker News Actors on the Store | Varies | Some cover feeds (top, new, best) or user profiles, which this Actor doesn't. |
| HN Search API by Algolia, or the official Firebase HN API | Free | Best if you want to write and host your own paging, parsing and scheduling. |

### Integrations and API

The actor ID is `fecklesspleb/hacker-news-scraper` (in API URLs: `fecklesspleb~hacker-news-scraper`).

**n8n, Make and Zapier.** Each has an Apify integration with a "run Actor" action. Pick `fecklesspleb/hacker-news-scraper`, paste the input JSON shown below, then read the results from the run's default dataset in the next step. To process only new items on a schedule, turn on the monitoring option and run it from a schedule (Apify Schedules or a trigger in your automation tool).

**Webhooks and schedules.** Apify Schedules can run the Actor on a cron, and a webhook on "run succeeded" can call your own endpoint with the run ID.

**HTTP API.** Run the Actor and get the items back in one call. This waits for the run to finish, so use it for small runs:

```bash
curl -X POST "https://api.apify.com/v2/acts/fecklesspleb~hacker-news-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"mode": "search", "query": "AI agents", "tags": ["story"], "minPoints": 50, "postedWithinDays": 30, "maxItems": 200}'
```

**Python** (`pip install apify-client`):

```python
from decimal import Decimal
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("fecklesspleb/hacker-news-scraper").call(
    run_input={
        "mode": "search",
        "query": "AI agents",
        "tags": [
            "story"
        ],
        "minPoints": 50,
        "postedWithinDays": 30,
        "maxItems": 200
    },
    max_total_charge_usd=Decimal("5"),  # optional spend cap
)
## apify-client 3.x returns a Run object; on 1.x/2.x use run["defaultDatasetId"]
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)
```

**JavaScript / Node.js** (`npm install apify-client`):

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('fecklesspleb/hacker-news-scraper').call(
    {
        "mode": "search",
        "query": "AI agents",
        "tags": [
            "story"
        ],
        "minPoints": 50,
        "postedWithinDays": 30,
        "maxItems": 200
    },
    { maxTotalChargeUsd: 5 }, // optional spend cap
);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

Results can also be exported from the dataset as JSON, CSV, Excel, XML or RSS from the Apify Console or the dataset API.

### FAQ

**How do I scrape "Ask HN: Who is hiring?" for a specific month?**
Set `mode` to `whoIsHiring` and `threads` to how many recent months you want (1 = the latest). Each top-level post becomes one job item. Filter with `keywords` and `remoteOnly`.

**Is the "Who is hiring?" parsing reliable?**
It follows the convention most posters use: `Company | Role | Location | Remote | Salary` on the first line. Posts that don't follow it still come through with `headline` and `text`, but `company`, `role` or `location` may be null. Check the text for anything critical.

**Why are emails removed?**
To avoid collecting personal contact details by default. Emails become `[email removed]` and phone numbers `[phone removed]`. You can turn this off with `redactContactDetails: false` if you have a lawful reason to keep them.

**Can I search comments mentioning my product?**
Yes: set `tags` to `["comment"]` and `query` to your product name, and turn on `onlyNewItems` for a daily alert.

**Does it return the HN front page?**
Use `tags: ["story", "front_page"]` for stories that Algolia tags as front page. There is no separate top, new or best feed mode.

**Do I need an API key or login?**
No. The Actor reads public endpoints and needs no account on the source site. You only need an Apify account to run it. It obeys robots.txt and rate-limits requests per host.

### Related Actors

- [RSS Feed Reader](https://apify.com/fecklesspleb/rss-feed-reader): RSS and Atom feed items as clean JSON, with feed discovery from a website URL.
- [Sitemap Extractor](https://apify.com/fecklesspleb/sitemap-extractor): every URL listed in a website's XML sitemaps.
- [ATS Jobs Scraper](https://apify.com/fecklesspleb/ats-jobs-scraper): jobs from Greenhouse, Lever, Ashby, Workable, Recruitee and Workday boards in one schema.
- [Remote Jobs Aggregator](https://apify.com/fecklesspleb/remote-jobs-aggregator): remote jobs from Remote OK, We Work Remotely and Jobicy, de-duplicated.

# Actor input Schema

## `mode` (type: `string`):

Search HN, or parse the monthly 'Ask HN: Who is hiring?' threads into jobs.

## `query` (type: `string`):

Words to search for (search mode). Leave empty to list the newest items.

## `tags` (type: `array`):

Search mode. Values: story, comment, ask_hn, show_hn, job, poll, front_page. Separate entries must ALL match (AND), e.g. story + front_page = front-page stories. For any of several types, put them in one entry separated by commas, e.g. 'story,comment'. Default: story.

## `sortBy` (type: `string`):

Newest first, or best match first.

## `minPoints` (type: `integer`):

Only items with at least this many points.

## `minComments` (type: `integer`):

Only stories with at least this many comments.

## `postedWithinDays` (type: `integer`):

Only items from the last N days.

## `since` (type: `string`):

Only items on or after this date (YYYY-MM-DD).

## `until` (type: `string`):

Only items before this date (YYYY-MM-DD).

## `threads` (type: `integer`):

1 = the latest month only.

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

Keep job posts that mention any of these words, e.g. python, rust, machine learning.

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

Keep only posts that say remote in the headline.

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

Stop after this many results.

## `redactContactDetails` (type: `boolean`):

Replaces emails and phone numbers in post text.

## `onlyNewItems` (type: `boolean`):

Skip items returned by earlier runs that used the same state store.

## `stateStoreName` (type: `string`):

Named key-value store that remembers seen items.

## Actor input object example

```json
{
  "mode": "search",
  "query": "AI agents",
  "tags": [
    "story"
  ],
  "sortBy": "date",
  "threads": 1,
  "remoteOnly": false,
  "maxItems": 100,
  "redactContactDetails": true,
  "onlyNewItems": false,
  "stateStoreName": "hacker-news-scraper-state"
}
```

# Actor output Schema

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

No description

## `summary` (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 = {
    "query": "AI agents",
    "tags": [
        "story"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("fecklesspleb/hacker-news-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 = {
    "query": "AI agents",
    "tags": ["story"],
}

# Run the Actor and wait for it to finish
run = client.actor("fecklesspleb/hacker-news-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 '{
  "query": "AI agents",
  "tags": [
    "story"
  ]
}' |
apify call fecklesspleb/hacker-news-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,fecklesspleb/hacker-news-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/u2aNFNxhT0Yf0YrH8/builds/12rk6OuQk8EaghNxr/openapi.json
