# Hacker News API: Search, Comments, Users & Who Is Hiring (`insight.solutions/hacker-news-api`) Actor

Everything public on Hacker News through its two official APIs: keyword search over 2007-present, the six feeds, full comment trees flattened with depth, user profiles, and the monthly Who Is Hiring thread parsed into structured jobs. Plus a monitor that bills only new hits. No API key.

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

## Pricing

from $0.12 / 1,000 item returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#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 API: Search, Comments, Users & Who Is Hiring

Everything Hacker News makes public, in **one schema**: keyword search over
2007–present, the six ranked feeds, whole comment trees flattened with depth and
parent, user profiles, and the monthly *"Ask HN: Who is hiring?"* thread parsed
into structured job rows with a confidence score on every one.

Read straight from the two APIs Hacker News publishes — the **HN Search API**
and the **official Firebase API**. No key, no login, no browser, and
`news.ycombinator.com` HTML is never fetched.

> **Why not the HTML?** The front page took **16.5 seconds and 34 KB** to load as
> a web page. The same thirty items came back from the search API in **1.0
> second and 32 KB**, already structured. Scraping the site would be slower, more
> fragile and ruder.

### At a glance

**Input** — this is the Store prefill; paste it and run:

```json
{ "mode": "search", "keywords": ["web scraping"], "tags": ["story"], "sort": "date", "minPoints": 20, "maxItems": 50 }
```

Fifty newest stories matching "web scraping" with at least twenty points. Change
`mode` for the other five: `feed`, `items`, `users`, `whoIsHiring`, `monitor`.

**Output** — one row per item; the fields you will use most are `title`,
`author`, `points`, `numComments`, `createdAt` and `hnUrl` (full list under
*Output reference*). Comments, users and parsed jobs come back on the same
columns. Anything that could not be fetched comes back as a free diagnostic row
(`rowType: "diagnostic"`, `errorType`, `error`) instead of a charge.

**Price** — $0.20 per 1,000 items on the FREE tier (+ $0.001 per run, charged
only once a run has returned an item); summary rows, diagnostic rows, hiring
comments below `minParseConfidence`, rows dropped by a filter, ids that do not
exist, dead or deleted items and blocked requests are all free, and a run that
returns nothing bills nothing at all. Pay-per-event, no API key, no browser,
limited permissions — works over the Apify MCP server (`mcp.apify.com`) and with
agentic (x402) payments.

**From code** —
`client.actor("insight.solutions/hacker-news-api").call(run_input={…})` with
`apify-client`, or `POST
https://api.apify.com/v2/acts/insight.solutions~hacker-news-api/run-sync-get-dataset-items`.

***

### What you get

One row per item. Same columns whatever the item is.

A **story**:

```json
{
  "rowType": "story",
  "source": "algolia",
  "id": 4058874,
  "objectId": "4058874",
  "type": "story",
  "title": "HackerNews API: What if HN does not have API? Make API on the fly with APIfy",
  "url": "http://apify.heroku.com/resources/4fca535156983f0001000002",
  "domain": "apify.heroku.com",
  "author": "sathish316",
  "points": 132,
  "numComments": 49,
  "createdAt": "2012-06-02T19:16:33.000Z",
  "createdAtTimestamp": 1338664593,
  "tags": ["story", "author_sathish316", "story_4058874"],
  "isShowHn": false,
  "isAskHn": false,
  "isLaunchHn": false,
  "isFrontPage": false,
  "matchedKeywords": ["hacker news api"],
  "rank": 1,
  "hnUrl": "https://news.ycombinator.com/item?id=4058874"
}
```

A **comment**, flattened out of the tree with its position in it:

```json
{
  "rowType": "comment",
  "id": 49718266,
  "author": "sandcat_",
  "text": "That isn't the point being discussed. The point being discussed is that…",
  "createdAt": "2026-09-15T20:16:16.000Z",
  "depth": 2,
  "parentId": 49717894,
  "storyId": 49716176,
  "storyTitle": "An Update on Wayback Machine Access",
  "storyUrl": "https://blog.archive.org/2026/09/15/an-update-on-wayback-machine-access/",
  "points": null,
  "hnUrl": "https://news.ycombinator.com/item?id=49718266"
}
```

A **job**, parsed out of one *Who is hiring?* comment:

```json
{
  "rowType": "job",
  "jobSource": "who-is-hiring",
  "threadId": 49522897,
  "threadTitle": "Ask HN: Who is hiring? (September 2026)",
  "threadMonth": "2026-09",
  "company": "Modash.io",
  "companyUrl": "https://modash.io",
  "roles": ["Senior Product Engineer"],
  "location": "Remote (Europe)",
  "remote": "remote",
  "remoteRegions": ["Europe"],
  "employmentType": "Full-time",
  "salaryRaw": "€75k–110k",
  "salaryMin": 75000,
  "salaryMax": 110000,
  "salaryCurrency": "EUR",
  "salaryPeriod": "year",
  "techStack": ["typescript", "react", "aws"],
  "applyUrl": "https://apply.workable.com/modash/j/C1507B65C3",
  "author": "thelastofthelas",
  "posterKarma": 4821,
  "posterAccountAgeDays": 4571,
  "posterTrust": "established",
  "firstLine": "Modash.io | Senior Product Engineer | Remote (Europe) | Full-time | €75k–110k | https://modash.io",
  "parseStatus": "parsed",
  "parseConfidence": 1,
  "hnUrl": "https://news.ycombinator.com/item?id=49522903"
}
```

A **user**:

```json
{
  "rowType": "user",
  "source": "firebase",
  "username": "pg",
  "karma": 157316,
  "about": "Bug fixer.",
  "createdAt": "2006-10-09T18:21:32.000Z",
  "accountAgeDays": 7281,
  "submissionCount": 15565,
  "submittedIds": [49705123, 49690012],
  "hnUrl": "https://news.ycombinator.com/user?id=pg"
}
```

`author`, `createdAt`, `points` and `hnUrl` mean the same thing on every one of
them. That is the point: **"everything on Hacker News about X" is one query, not
five endpoints and a join.**

***

### The numbers, because nobody else publishes any

**On the September 2026 *Who is hiring?* thread, 247 of 264 top-level comments —
93.6% — parsed into a company**, and every job row carries a `parseConfidence`
from 0 to 1 so you can filter. The other 17 come back **free**, with their full
text, so you can see exactly what the parser left behind. The score separates
cleanly: 247 rows at 0.6 or above, 17 at 0.2 or below, and nothing in between.

Of the 247 parsed postings: 91.7% had three or more fields in the header line,
87.5% carried a REMOTE/ONSITE/HYBRID token, 26.5% stated pay, 85.2% carried a
link and 32.2% carried an email — 15 of them written as `jobs[at]example.com`,
which is reassembled.

***

### Quick start

Leave everything on its defaults and run it: you get the fifty most recent
Hacker News stories matching "web scraping" with more than twenty points, in a
couple of seconds, for about a cent.

| You want | Input |
|---|---|
| Every mention of your product, newest first | `{ "keywords": ["acme"], "tags": ["story", "comment"], "tagsMode": "any" }` |
| The front page right now | `{ "mode": "feed", "feeds": ["front"] }` |
| Show HN launches this month | `{ "keywords": [], "tags": ["show_hn"], "dateFrom": "30d" }` |
| A whole discussion, comments and all | `{ "mode": "items", "itemUrls": ["https://news.ycombinator.com/item?id=49716176"] }` |
| This month's hiring thread as jobs | `{ "mode": "whoIsHiring" }` |
| Six months of remote Rust jobs | `{ "mode": "whoIsHiring", "monthsBack": 6, "remoteOnly": true, "filterKeywords": ["rust"] }` |
| Everything pg has posted lately | `{ "mode": "users", "usernames": ["pg"], "includeSubmissions": true }` |
| A daily alert on your company name | `{ "mode": "monitor", "keywords": ["acme"] }` on a schedule |

***

### Use cases

- **Track a product, a company or a person across all of Hacker News** — one `keywords` query returns the stories *and* the comments in the same columns, deduplicated, with `matchedKeywords` on every row.
- **Build a job board from the hiring thread** — `whoIsHiring` turns a 264-comment thread into 247 structured rows with company, roles, location, remote status, salary, tech stack and an apply link, plus a confidence score you can threshold on.
- **Analyse a discussion** — `items` returns a whole comment tree flattened, with `depth`, `parentId` and `storyId`, which is the shape you want for a thread graph or a sentiment pass.
- **Run a cheap daily alert** — `monitor` remembers what it returned and bills only what is new. A quiet day costs nothing and finishes SUCCEEDED.
- **Feed a dataset or an agent** — one flat JSON array, no key, no browser, no rate limit to manage.

***

### Input

| Field | Type | Default | What it does |
|---|---|---|---|
| `mode` | enum | `search` | `search`, `feed`, `items`, `users`, `whoIsHiring`, `monitor`. Explicit, so nothing switches silently. Inputs belonging to other modes are ignored, not errors |
| `keywords` | string\[] | `[]` | One query each; results deduplicated across them |
| `tags` | string\[] | `["story"]` | `story`, `comment`, `poll`, `show_hn`, `ask_hn`, `launch_hn`, `front_page`, `job`, or `author_<name>` / `story_<id>` |
| `tagsMode` | enum | `all` | `all` = AND, `any` = OR |
| `authors` | string\[] | `[]` | Only these usernames. **Case-sensitive upstream** |
| `sort` | enum | `date` | `date` (newest first) or `relevance` |
| `dateFrom` / `dateTo` | string | — | `2026-01-01`, `2026-01-01T12:00:00Z`, or `7d` / `30d` / `12h` |
| `minPoints` / `maxPoints` | int | — | Stories only — Hacker News does not publish comment scores |
| `minComments` / `maxComments` | int | — | Stories only, same reason |
| `excludeKeywords` | string\[] | `[]` | Dropped before the charge |
| `hitsPerPage` | int | 100 | Results per request, max 1000 |
| `feeds` | enum\[] | `["front"]` | `front`, `new`, `best`, `ask`, `show`, `jobs` |
| `maxItemsPerFeed` | int | 30 | How far down each feed to go |
| `itemIds` / `itemUrls` | string\[] | `[]` | Ids, or `item?id=…` / `…/items/…` links |
| `includeComments` | bool | on in `items`, off in `feed` | Return the whole comment tree |
| `maxCommentsPerStory` | int | 50 | 0 means every comment |
| `maxCommentDepth` | int | 10 | 1 is top-level only |
| `commentsMinLength` | int | 0 | Drop short comments before the charge |
| `usernames` | string\[] | `[]` | **Case-sensitive** |
| `includeSubmissions` | bool | false | Also return recent stories and comments |
| `maxSubmissionsPerUser` | int | 20 | Newest first |
| `submissionTypes` | enum\[] | both | `story`, `comment` |
| `hiringThread` | enum | `who-is-hiring` | Or `who-wants-to-be-hired`, `freelancer` |
| `months` | string\[] | `[]` | `["2026-09"]`, matched on the thread's title |
| `monthsBack` | int | 1 | Used when `months` is empty |
| `threadIds` | string\[] | `[]` | Specific threads |
| `maxJobsPerThread` | int | 300 | A busy month is ~264 postings |
| `remoteOnly` | bool | false | Applied before the charge |
| `salaryMin` | int | — | Applied before the charge |
| `filterKeywords` | string\[] | `[]` | Applied before the charge |
| `minParseConfidence` | number | 0.3 | Below this, a posting is free with its full text |
| `includeUnparsed` | bool | true | Return the free ones too |
| `enrichPoster` | bool | true | Adds karma, account age and a trust label |
| `includeReplies` | bool | false | Replies are discussion, not job posts |
| `stateStoreName` | string | `hacker-news-api-state` | The monitor's memory |
| `firstRunBehavior` | enum | `emit-all` | Or `baseline-only` (free) |
| `lookbackHours` | int | 24 | Used when the monitor has no state yet |
| `maxItems` | int | 200 | 0 means everything, up to the 5,000-row run ceiling |
| `includeText` | bool | true | `text` and `textHtml` on stories and comments |
| `includeSummary` | bool | true | The free summary rows |
| `maxConcurrency` | int | 4 | Targets in flight |
| `maxRunSecs` | int | 240 | Time budget, max 3600 |
| `proxyConfiguration` | object | `{ "useApifyProxy": true }` | Datacenter. Both APIs answer it |

***

### Output reference

`rowType` is one of six. Four are charged, two are free.

| `rowType` | Charged | What it is |
|---|---|---|
| `story` | yes | A story, a Show HN, an Ask HN or a Launch HN |
| `comment` | yes | One comment, with `depth`, `parentId` and `storyId` |
| `user` | yes | One profile |
| `job` | yes | One YC job post, or one parsed *Who is hiring?* posting |
| `summary` | **no** | What the run did: hit counts, feed lengths, parse rates, monitor state |
| `diagnostic` | **no** | Anything that could not be read or could not be parsed |

Every row carries `ok`, `rowType`, `input`, `error`, `errorType`, `scrapedAt`,
`source`, `sourceUrl`, and `null` in every column that does not apply — so a CSV
export is one rectangular table however the run was configured.

#### Identity and time, normalised

The two APIs disagree about both, so this Actor does not pass either through.
`id` is always a number and `objectId` always the string the search API returns.
`createdAt` is always an ISO-Z string and `createdAtTimestamp` always epoch
seconds, whether the value arrived as `2026-09-15T20:16:16Z` (search),
`2026-09-01T15:01:17.000Z` (the item endpoint) or `1789503376` (the official
API).

#### Free summary rows

| `summaryType` | Carries |
|---|---|
| `run-summary` | Which mode ran and with which inputs — the first row in the dataset — and, at the end, the request count and how many rows were billed |
| `query-summary` | `nbHits`, `rowsReturned`, `rowsFiltered`, `slices`, `truncated` |
| `feed-summary` | `feedLength` — how long the feed *really* is today |
| `thread-summary` | `commentsReported` vs `commentsReturned`, `jobsParsed`, `jobsUnparsed`, `parseRate`, `confidenceHistogram` |
| `user-summary` | `submissionCount` and how many submissions came back |
| `monitor-summary` | `firstRun`, `newItems`, `alreadySeen`, `newestSeen`, `windowFrom`, `newLast7Days`, `newLast30Days`, `historyDays` |

#### Diagnostic rows

`errorType` is one of `not-found`, `no-results`, `unparsed`, `invalid-input`,
`blocked`, `timeout`, `truncated`, `unavailable`. None of them is ever charged
for, and a run that produced only these finishes **FAILED** with a status message
saying so.

***

### The 1,000-result window

Hacker News search returns **at most 1,000 results per query**, however you page
it. A query with 1,019 matches at 50 per page gives 20 pages, not 21, and asking
for page 21 returns Algolia's own error.

When your query matches more than that and you asked for more, **this Actor walks
backwards through time**: it takes the oldest result in the window it just read
and re-issues the query for everything strictly older, repeating until it has
what you asked for, the date range runs out, or the time budget does. The slices
are disjoint and gapless. The free `query-summary` row tells you how many slices
it took and whether it still had to stop.

`nbHits` is an **estimate**, and this Actor never derives a page count from it:
the same query reported 285 matches on one page and 278 on the next, one second
apart.

***

### Freshness, honestly

The search index picks up a new post in seconds to minutes. The six feeds come
from Hacker News's own API and are live. **Nothing here is real-time and nothing
claims to be.** If you need a post the moment it appears, poll the `new` feed;
if you need everything about a keyword, use search and accept the small lag.

***

### Coverage, as numbers

- **Search** covers 2007 to now, stories, comments, polls, Ask HN, Show HN,
  Launch HN and job posts.
- **The front page** is 30 items. `topstories` is 500 and `newstories` is 500;
  `beststories` was 200, `showstories` 185, `askstories` **28** and `jobstories`
  **31** on the day this was measured. Those are as many as Hacker News
  currently lists, not a truncation — the free `feed-summary` row carries the
  real number so you can tell the difference.
- **One hiring thread** is around 264 postings and 401 comments.
- **One comment tree** arrives in a single request, however deep: the September
  2026 hiring thread is 385 nodes and nine levels, and took 2.7 seconds.

***

### What you are never charged for

- Every `summary` row, of every kind.
- Every `diagnostic` row.
- Hiring comments below `minParseConfidence` — they come back free, with their
  full text, `author` and a link.
- Items removed by a client-side filter (`excludeKeywords`, `remoteOnly`,
  `salaryMin`, `filterKeywords`, `commentsMinLength`).
- Ids and usernames that do not exist.
- Items Hacker News has marked dead or deleted.
- Requests that were blocked or timed out.
- A run that returns nothing at all: it finishes **FAILED** and bills nothing,
  **start fee included**.

***

### Pricing

| Event | What it is | FREE | BRONZE | SILVER | GOLD |
|---|---|---|---|---|---|
| `actor-start` | Charged once per run, and only after the run has returned at least one item | $0.001 | $0.001 | $0.001 | $0.001 |
| **`item`** | One story, comment, user profile or parsed job | **$0.0002** | **$0.0002** | **$0.00016** | **$0.00012** |

**$0.20 per 1,000 items.** One price for every kind of row, so you never have to
work out which surface a row came from to predict a bill.

| Run | Cost |
|---|---|
| The default input — 50 stories | **$0.011** |
| One hiring thread — 247 parsed jobs, 17 free | **$0.0504** |
| The front page with every comment — ~930 rows | **$0.187** |
| A daily monitor finding 5 new mentions | **$0.002** — about **$0.06 a month** |
| A monitor day with nothing new | **$0.00** |

Charging is charge-after-push: rows are in your dataset before the event is
recorded. `ACTOR_MAX_TOTAL_CHARGE_USD` is respected — the run stops fetching when
it is reached and finishes SUCCEEDED saying so.

***

### Proxy

The default is `{ "useApifyProxy": true }` — Apify's **datacenter** pool. Both
Hacker News APIs answered every request from a datacenter exit *and* from no
proxy at all in the capture probe (`topstories.json` came back byte-identical
either way), so residential buys nothing here and costs about forty times as
much. You can switch to residential if your account needs it; you will not get
different data.

The run holds itself to **5 requests a second** against the search API and **10**
against the official one, well inside the 10,000-per-hour-per-IP ceiling the
search API documents, and pauses 250–600 ms between successive pages of one
query. A 429 is treated as a block: the proxy session is rotated once, and a
second refusal becomes a free `blocked` row rather than a retry loop.

***

### Running it every day

Set `mode: "monitor"`, give it your keywords and put it on a schedule. Each run
asks the search API for everything posted since the last run's newest result —
normally **one request per keyword** — and returns only what is new.

**The first run.** Against an empty store, `firstRunBehavior: "emit-all"` (the
default) returns and bills up to `maxItems` matches, so the first run is useful
immediately. Set it to `baseline-only` and the first run returns nothing, bills
nothing and just records where to start; every run after that bills only what is
new either way.

**If a run finds more than `maxItems`**, it returns the oldest of them and leaves
the cursor where it stopped, so the rest arrive on the next run rather than being
skipped. The `monitor-summary` row sets `truncated` when that happens.

#### Where the state lives

In a **named key-value store** (`stateStoreName`, default
`hacker-news-api-state`), not in the run's own storage — a run's default store is
created fresh every time, so anything kept there would be gone by tomorrow.

This works under **limited permissions** because the Actor creates that store
itself on its first run and re-opens it on its own later runs, which is exactly
what Apify's permission model allows. If the store cannot be read, the run
degrades to first-run behaviour — a fresh baseline — rather than to a wave of
duplicate rows.

Two runs sharing one store would each diff against the same snapshot and charge
you twice for the same items, so the second one **stops** with a free diagnostic
row instead. The snapshot keeps the newest timestamp it has seen plus up to
20,000 recent ids (which catch anything the index publishes late), and query
snapshots that stop being used are dropped after 90 days.

***

### Use it from an AI agent, or from code

One JSON object in, one flat array out — the shape agent runtimes want. The Actor runs with **limited permissions**, uses **pay-per-event** pricing and never enters Standby, so it works over the Apify MCP server and with x402 agentic payments. The **Integrations** tab pushes results to Slack, a webhook, Zapier, Make, Google Sheets, Snowflake or BigQuery.

```bash
curl -X POST "https://api.apify.com/v2/acts/insight.solutions~hacker-news-api/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"mode":"search","keywords":["rust"],"tags":["story"],"dateFrom":"7d","minPoints":50,"maxItems":100}'
```

```python
## pip install apify-client
from apify_client import ApifyClient

client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("insight.solutions/hacker-news-api").call(run_input={
    "mode": "whoIsHiring",
    "monthsBack": 3,
    "remoteOnly": True,
    "filterKeywords": ["python", "rust"],
    "maxItems": 500,
})

for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    if row.get("ok") and row["rowType"] == "job":
        print(row["company"], row["roles"], row["location"],
              row["salaryRaw"], row["applyUrl"] or row["email"],
              row["parseConfidence"], sep=" | ")
```

Filter on `parseConfidence` when you are going to act on the rows without reading
them: at `>= 0.9` you are looking at postings that used the thread's convention
in full.

***

### FAQ

**Do I need an API key?**
No. Both upstream APIs are public and unauthenticated, and this Actor sends no
cookies and no credentials of anybody's.

**Why do comments have no `points`?**
Because Hacker News does not publish comment scores. There is no endpoint that
has them, so the column is null rather than guessed at — and `minPoints` on a
comments-only search comes back as a free `invalid-input` row explaining why,
instead of silently returning nothing.

**Why does a thread report more comments than it returns?**
The September 2026 hiring thread reports 401 comments and the search index holds
384 of them. The difference is comments that were deleted, flagged or killed,
which the index does not carry. The free `thread-summary` row prints both numbers
so you always know which you are looking at.

**Why is `author` case-sensitive?**
Because Hacker News usernames are. `pg` is an account and `PG` is not. The Actor
does not fold case, because folding it would silently return the wrong person's
posts or none at all.

**How accurate is the hiring parser?**
On the September 2026 thread, 247 of 264 top-level comments — 93.6% — produced a
company. Every row carries `parseConfidence`, and the 17 that did not parse come
back free with their full text so you can judge them yourself. There is no model
and no language detection anywhere in it: the one Dutch posting in that thread
parsed correctly because the rules are structural, not linguistic.

**Can I get more than 1,000 results for one query?**
Yes — the Actor walks backwards through time in slices and keeps going. See
*The 1,000-result window*.

**What happens if the format changes?**
The search index is operated by a third party and its repository was archived in
February 2026. If a response stops parsing, the run returns a free diagnostic row
naming the request rather than a wrong number, and only finishes successfully if
it returned at least one real item.

**Is this affiliated with Hacker News or Y Combinator?**
No. It reads two public APIs that Hacker News publishes and links back to every
item on news.ycombinator.com.

***

### Limitations

- **Comment scores are not public.** There is no `points` on a comment, from any
  source.
- **The comment tree omits dead and deleted comments.** 401 reported against 384
  returned on the September 2026 thread. Both numbers are on the summary row.
- **Author and username filters are case-sensitive**, because Hacker News is.
- **Search is not real-time.** New posts appear in the index within seconds to
  minutes. The feeds are live; search is not.
- **The hiring parser reads a convention, not a schema.** It is a thread of free
  text that mostly follows a format. `parseConfidence` is how much of that format
  a given posting used, and the ones that used none of it are returned free
  rather than guessed at.
- **Salary is only there when the poster wrote it** — about a quarter of
  postings. `salaryMin` excludes everything with no stated pay.
- **`hitsPerPage` above 50 is documented but unverified here.** Every capture
  this Actor was built against used 50 or fewer; the documented maximum is 1,000.
- **The upstream format may change.** The search index is a third party's and its
  repository was archived in February 2026.

***

### Our other Actors

Every Insight Solutions Actor is pay-per-result with no browser, no login and no API key, and every one of them returns free diagnostic rows instead of billing for failures. Prices are per 1,000 results.

**Video, audio & social**

- [YouTube Transcript API](https://apify.com/insight.solutions/youtube-transcript-api) — captions as timed segments, text, SRT or VTT, with language fallback and translation.
- [YouTube Comments API](https://apify.com/insight.solutions/youtube-comments-api) — comments and replies with likes, pinned and hearted flags, newest or top sort.
- [YouTube Channel API](https://apify.com/insight.solutions/youtube-channel-api) — a channel's videos, Shorts and live streams, plus YouTube search.
- [Podcast Search, Episodes & Charts API](https://apify.com/insight.solutions/podcast-api) — Apple Podcasts search, charts and full episode feeds.
- [Bluesky Scraper](https://apify.com/insight.solutions/bluesky-scraper) — profiles, posts, followers and follows from the public AT Protocol API.
- [Telegram Channel Scraper](https://apify.com/insight.solutions/telegram-channel-scraper) — posts, views and channel stats from public Telegram channels.
- [Substack Scraper](https://apify.com/insight.solutions/substack-scraper) — posts with full free text, comments and publication profiles.

**News, documents & the web**

- [Google News Search, Topics & Real Article URLs](https://apify.com/insight.solutions/google-news-api) — news search and topic feeds with the publisher's real URL decoded.
- [Website to Markdown — Content Extractor for LLMs & RAG](https://apify.com/insight.solutions/website-content-extractor) — any site as clean Markdown, text and heading-aware chunks.
- [Internet Archive API](https://apify.com/insight.solutions/internet-archive-api) — archive.org search, item metadata, files and reviews.
- [Wayback Machine Toolkit](https://apify.com/insight.solutions/wayback-toolkit) — archived URL inventories, snapshots and text diffs between dates.
- [Website Technology Detector](https://apify.com/insight.solutions/website-tech-detector) — the tech stack behind any site, with the evidence for each detection.
- [Domain Intelligence API](https://apify.com/insight.solutions/domain-intelligence-api) — DNS, RDAP registration, TLS certificate and HTTP facts in one row per domain.
- [SEO Page Audit](https://apify.com/insight.solutions/seo-page-audit) — sitemap crawl with on-page checks, structured data and broken-link reports.
- [Keyword Suggestions API](https://apify.com/insight.solutions/keyword-suggestions-api) — Google, YouTube, Bing, Amazon and eBay autocomplete with alphabet and question expansions.
- [Website Contact Extractor](https://apify.com/insight.solutions/website-contact-extractor) — emails, phone numbers and social profiles from any list of websites.
- [Web Search Results API](https://apify.com/insight.solutions/web-search-api) — Bing and DuckDuckGo organic results with snippets, no key, no browser.
- [Company Enrichment API](https://apify.com/insight.solutions/company-enrichment-api) — a domain in, a company profile out: firmographics, contacts, tech stack, DNS and hiring signal.

**Business, finance & jobs**

- [Congress & Insider Trades API](https://apify.com/insight.solutions/congress-insider-trades-api) — STOCK Act periodic transaction reports and SEC Form 4 insider trades in one schema.
- [Federal Contracts, Grants & Lobbying API](https://apify.com/insight.solutions/federal-contracts-grants-api) — SAM.gov opportunities, USAspending awards, Grants.gov notices and Senate lobbying filings in one schema.
- [SEC EDGAR API](https://apify.com/insight.solutions/sec-edgar-api) — filings, XBRL financials and full-text search by ticker or CIK.
- [Clinical Trials & FDA API](https://apify.com/insight.solutions/clinical-trials-fda-api) — ClinicalTrials.gov studies plus openFDA recalls, labels, approvals, 510(k)s and adverse-event reports.
- [Product & Vehicle Recalls API](https://apify.com/insight.solutions/product-recalls-api) — CPSC, NHTSA, FDA and USDA recalls, vehicle complaints and ratings, plus a VIN decoder.
- [Y Combinator Companies, Batches & Founders](https://apify.com/insight.solutions/yc-companies-directory) — the YC directory with founders and social links, filterable by batch, industry and hiring status.
- [Career Site Jobs API](https://apify.com/insight.solutions/ats-jobs-api) — jobs straight from Greenhouse, Lever, Ashby, Workable and 10+ other ATS career sites.
- [New Job Postings Monitor](https://apify.com/insight.solutions/job-postings-monitor) — new, closed and changed postings on the career sites you watch.
- [Remote Jobs API](https://apify.com/insight.solutions/remote-jobs-api) — RemoteOK, Remotive, We Work Remotely, Himalayas, Jobicy and more in one schema, deduplicated.
- [Shopify Products API](https://apify.com/insight.solutions/shopify-products-api) — any Shopify store's catalogue, variants, prices and stock signals.
- [Shopify Store Monitor](https://apify.com/insight.solutions/shopify-store-monitor) — price drops, sales, restocks, sell-outs and new products on any Shopify store, one row per change.

**Apps & games**

- [App Store & Google Play Reviews API](https://apify.com/insight.solutions/app-reviews-api) — reviews from both stores with ratings, versions and developer replies.
- [App Store Top Charts & App Search API](https://apify.com/insight.solutions/app-charts-api) — Apple top charts by country and genre, plus app search and details.
- [App Store Keyword Rank Tracker](https://apify.com/insight.solutions/app-store-keyword-rank-tracker) — where any app ranks for any keyword on the App Store and Google Play, with rank changes and ASO suggestions.
- [Steam Reviews API](https://apify.com/insight.solutions/steam-reviews-api) — Steam reviews with playtime, helpfulness and game details.
- [Steam Game Data API](https://apify.com/insight.solutions/steam-store-stats-api) — prices, tags, review scores, live player counts and top charts.

# Actor input Schema

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

What to read. `search` runs keyword searches over everything Hacker News has indexed since 2007. `feed` pulls one of the six ranked lists (front page, new, best, Ask, Show, jobs). `items` returns specific stories and their whole comment trees. `users` returns profiles. `whoIsHiring` parses the monthly "Ask HN: Who is hiring?" thread into structured jobs. `monitor` returns only what is new since your last run. Inputs belonging to the other modes are ignored, not errors.

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

One Algolia query per entry, e.g. `["web scraping", "rust"]`. Results are deduplicated across keywords and every row lists the keywords that matched it in `matchedKeywords`. Leave empty for "everything the other filters allow". Used by `search` and `monitor`.

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

Which kinds of item to return. `story`, `comment`, `poll`, `show_hn`, `ask_hn`, `launch_hn`, `front_page` and `job` are Hacker News's own tags. You can also type `author_<username>` or `story_<id>` by hand. Used by `search` and `monitor`.

## `tagsMode` (type: `string`):

`all` requires every tag (`story` **and** `show_hn`). `any` accepts any of them (`story` **or** `poll`). Author filters are always required on top of whichever you pick, because "stories or polls by pg" is the only reading anybody means.

## `sort` (type: `string`):

`date` returns newest first and is the order to use when you are pulling a window of time. `relevance` returns Hacker News search's own ranking, which is not time-ordered.

## `feeds` (type: `array`):

Which ranked lists to pull, in Hacker News's own order. `front` is the top 500 stories, `new` the newest 500, `best` the top 200; `ask`, `show` and `jobs` are as long as Hacker News currently lists, which is often far shorter (28 and 31 on the day this was built). The free `feed-summary` row carries the real length. Used by `feed`.

## `maxItemsPerFeed` (type: `integer`):

How far down each feed to go. 30 is one front page.

## `itemIds` (type: `array`):

Specific Hacker News item ids, e.g. `["4058874"]`. Used by `items`.

## `itemUrls` (type: `array`):

Item links instead of ids — `https://news.ycombinator.com/item?id=4058874` or `https://hn.algolia.com/api/v1/items/4058874`. The id is read out of the link; the page is never fetched.

## `usernames` (type: `array`):

Hacker News usernames, e.g. `["pg"]`. **Case-sensitive.** Used by `users`.

## `includeSubmissions` (type: `boolean`):

Also return each user's most recent stories and comments as ordinary rows. They come from one search request, not from resolving the account's whole submission history — pg alone has 15,565 submissions.

## `hiringThread` (type: `string`):

Which of the three monthly threads to parse. All three are posted by the same account on the first working day of the month.

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

Which months' threads to parse, e.g. `["2026-09", "2026-08"]`. Matched against the `(Month YYYY)` in the thread's own title, not against when it was posted — the August 2026 thread went up on the 3rd. Leave empty and Months back is used instead.

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

How many recent threads to parse when Months is empty. 1 is the current month's.

## `lookbackHours` (type: `integer`):

How far back the monitor looks when it has no stored state yet.

## `firstRunBehavior` (type: `string`):

What the very first monitor run against an empty store does. `emit-all` returns and bills up to Max items; every later run bills only what is new. `baseline-only` returns nothing, bills nothing, and writes the baseline so the next run has something to compare against.

## `dateFrom` (type: `string`):

Only items posted on or after this. Accepts a date (`2026-01-01`), a full timestamp (`2026-01-01T12:00:00Z`) or a relative window (`7d`, `30d`, `12h`). A relative window means the same thing on every run of a schedule.

## `dateTo` (type: `string`):

Only items posted on or before this. Same formats as From. A plain date covers the whole day.

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

Only stories with at least this many points. Hacker News does not publish comment scores, so this cannot be applied to a comments-only search — ask for it and you get a free `invalid-input` row explaining why, and the rest of the query still runs.

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

The most paid rows this run may return. 0 means "everything the filters allow", up to the 5,000-row run ceiling. Free summary and diagnostic rows do not count against it.

## `maxPoints` (type: `integer`):

Only stories with at most this many points. Useful for finding things that were posted and went nowhere.

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

Only stories with at least this many comments. Stories only, for the same reason as Minimum points.

## `maxComments` (type: `integer`):

Only stories with at most this many comments.

## `authors` (type: `array`):

Only items by these usernames. Hacker News usernames are **case-sensitive**: `pg` and `PG` are not the same account, and only the first one exists.

## `excludeKeywords` (type: `array`):

Drop any row whose title, text or URL contains one of these. Applied before anything is charged, so excluded rows cost you nothing.

## `hitsPerPage` (type: `integer`):

How many results to ask for per request. Bigger pages mean fewer requests. Hacker News search returns at most 1,000 results per query however you page it; past that this Actor walks backwards through time in slices and tells you in a free summary row when it still had to stop.

## `includeComments` (type: `boolean`):

Return each story's whole comment tree as `comment` rows, with `depth`, `parentId` and `storyId` on every one. On by default in `items` mode, off by default in `feed` mode — a front page with comments is roughly 900 extra rows.

## `maxCommentsPerStory` (type: `integer`):

Cap on comment rows per story. 0 means every comment in the tree.

## `maxCommentDepth` (type: `integer`):

How deep into the reply tree to go. 1 is top-level comments only.

## `commentsMinLength` (type: `integer`):

Drop comments shorter than this many characters. Applied before the charge, so a run that only wants substantial comments does not pay for one-word replies.

## `maxSubmissionsPerUser` (type: `integer`):

How many recent submissions to return per user, newest first.

## `submissionTypes` (type: `array`):

Which of a user's submissions to return.

## `threadIds` (type: `array`):

Parse these specific thread ids instead of looking them up by month.

## `maxJobsPerThread` (type: `integer`):

Cap on postings read from each thread. A busy month is around 264 top-level comments.

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

Only postings whose first line carries a REMOTE token. Applied before the charge.

## `salaryMin` (type: `integer`):

Only postings whose parsed salary reaches this. Roughly a quarter of postings state pay at all; the rest have no salary to compare and are excluded when you set this.

## `filterKeywords` (type: `array`):

Only postings whose text contains one of these, e.g. `["rust", "golang"]`. Applied before the charge.

## `minParseConfidence` (type: `number`):

Every job row carries a `parseConfidence` from 0 to 1. Postings below this are returned free as diagnostics with their full text, never as paid rows. On the September 2026 thread the score was bimodal: 247 postings at 0.6 or above and 17 at 0.2 or below, with nothing in between.

## `includeUnparsed` (type: `boolean`):

Return the comments that did not parse as free diagnostic rows carrying their full text, so you can see what the parser left behind. Never charged either way.

## `enrichPoster` (type: `boolean`):

Add `posterKarma`, `posterCreated`, `posterAccountAgeDays` and `posterTrust` to every job row. One request per distinct poster, cached for the run. A failed lookup leaves the four columns null and never fails the row.

## `includeReplies` (type: `boolean`):

Also try to parse replies to postings. Off by default: of the 120 deeper replies in the September 2026 thread, exactly 2 looked like job posts. Replies are discussion.

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

Include `text` and `textHtml` on stories and comments. Turning it off keeps the rows small when you only want titles, scores and links.

## `includeSummary` (type: `boolean`):

Add the free summary rows: what this run was asked to do, how many hits each query had, how long each feed really is, how many postings parsed, and what the monitor saw. Never charged.

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

The named key-value store the monitor remembers previous runs in. Every scheduled run of the same monitor must use the same name. Letters, digits and dashes, 3–63 characters.

## `maxConcurrency` (type: `integer`):

How many targets to work on at once. The Actor holds itself to 5 requests a second against the search API and 10 against the official API whatever you set here.

## `maxRunSecs` (type: `integer`):

Stop after this many seconds. Rows already returned are kept and a free `timeout` row says what was left.

## `proxyConfiguration` (type: `object`):

Both Hacker News APIs answered every request from the Apify datacenter proxy and from no proxy at all, so datacenter is the default and residential buys nothing here at forty times the price.

## Actor input object example

```json
{
  "mode": "search",
  "keywords": [
    "web scraping"
  ],
  "tags": [
    "story"
  ],
  "tagsMode": "all",
  "sort": "date",
  "feeds": [
    "front"
  ],
  "maxItemsPerFeed": 30,
  "itemIds": [],
  "itemUrls": [],
  "usernames": [],
  "includeSubmissions": false,
  "hiringThread": "who-is-hiring",
  "months": [],
  "monthsBack": 1,
  "lookbackHours": 24,
  "firstRunBehavior": "emit-all",
  "minPoints": 20,
  "maxItems": 50,
  "authors": [],
  "excludeKeywords": [],
  "hitsPerPage": 100,
  "maxCommentsPerStory": 50,
  "maxCommentDepth": 10,
  "commentsMinLength": 0,
  "maxSubmissionsPerUser": 20,
  "submissionTypes": [
    "story",
    "comment"
  ],
  "threadIds": [],
  "maxJobsPerThread": 300,
  "remoteOnly": false,
  "filterKeywords": [],
  "minParseConfidence": 0.3,
  "includeUnparsed": true,
  "enrichPoster": true,
  "includeReplies": false,
  "includeText": true,
  "includeSummary": true,
  "stateStoreName": "hacker-news-api-state",
  "maxConcurrency": 4,
  "maxRunSecs": 240,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

Stories, comments, users and parsed jobs from the Hacker News Search API and the official Hacker News API, in one schema, plus free summary and diagnostic rows. Delivered as JSON items in the default dataset.

# 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 = {
    "mode": "search",
    "keywords": [
        "web scraping"
    ],
    "tags": [
        "story"
    ],
    "sort": "date",
    "minPoints": 20,
    "maxItems": 50,
    "includeText": false,
    "includeSummary": true,
    "maxConcurrency": 4,
    "maxRunSecs": 240,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("insight.solutions/hacker-news-api").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 = {
    "mode": "search",
    "keywords": ["web scraping"],
    "tags": ["story"],
    "sort": "date",
    "minPoints": 20,
    "maxItems": 50,
    "includeText": False,
    "includeSummary": True,
    "maxConcurrency": 4,
    "maxRunSecs": 240,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("insight.solutions/hacker-news-api").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 '{
  "mode": "search",
  "keywords": [
    "web scraping"
  ],
  "tags": [
    "story"
  ],
  "sort": "date",
  "minPoints": 20,
  "maxItems": 50,
  "includeText": false,
  "includeSummary": true,
  "maxConcurrency": 4,
  "maxRunSecs": 240,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call insight.solutions/hacker-news-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,insight.solutions/hacker-news-api"
        }
    }
}
```

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/Ed5j5QWYeq5bFoZ0s/builds/W3hgzAbG4b6CDm7Ex/openapi.json
