Hacker News API: Search, Comments, Users & Who Is Hiring avatar

Hacker News API: Search, Comments, Users & Who Is Hiring

Pricing

from $0.12 / 1,000 item returneds

Go to Apify Store
Hacker News API: Search, Comments, Users & Who Is Hiring

Hacker News API: Search, Comments, Users & Who Is Hiring

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.

Pricing

from $0.12 / 1,000 item returneds

Rating

0.0

(0)

Developer

Insight Solutions

Insight Solutions

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

a day ago

Last modified

Share

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:

{ "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 codeclient.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:

{
"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:

{
"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:

{
"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:

{
"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 wantInput
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 threadwhoIsHiring 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 discussionitems 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 alertmonitor 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

FieldTypeDefaultWhat it does
modeenumsearchsearch, feed, items, users, whoIsHiring, monitor. Explicit, so nothing switches silently. Inputs belonging to other modes are ignored, not errors
keywordsstring[][]One query each; results deduplicated across them
tagsstring[]["story"]story, comment, poll, show_hn, ask_hn, launch_hn, front_page, job, or author_<name> / story_<id>
tagsModeenumallall = AND, any = OR
authorsstring[][]Only these usernames. Case-sensitive upstream
sortenumdatedate (newest first) or relevance
dateFrom / dateTostring2026-01-01, 2026-01-01T12:00:00Z, or 7d / 30d / 12h
minPoints / maxPointsintStories only — Hacker News does not publish comment scores
minComments / maxCommentsintStories only, same reason
excludeKeywordsstring[][]Dropped before the charge
hitsPerPageint100Results per request, max 1000
feedsenum[]["front"]front, new, best, ask, show, jobs
maxItemsPerFeedint30How far down each feed to go
itemIds / itemUrlsstring[][]Ids, or item?id=… / …/items/… links
includeCommentsboolon in items, off in feedReturn the whole comment tree
maxCommentsPerStoryint500 means every comment
maxCommentDepthint101 is top-level only
commentsMinLengthint0Drop short comments before the charge
usernamesstring[][]Case-sensitive
includeSubmissionsboolfalseAlso return recent stories and comments
maxSubmissionsPerUserint20Newest first
submissionTypesenum[]bothstory, comment
hiringThreadenumwho-is-hiringOr who-wants-to-be-hired, freelancer
monthsstring[][]["2026-09"], matched on the thread's title
monthsBackint1Used when months is empty
threadIdsstring[][]Specific threads
maxJobsPerThreadint300A busy month is ~264 postings
remoteOnlyboolfalseApplied before the charge
salaryMinintApplied before the charge
filterKeywordsstring[][]Applied before the charge
minParseConfidencenumber0.3Below this, a posting is free with its full text
includeUnparsedbooltrueReturn the free ones too
enrichPosterbooltrueAdds karma, account age and a trust label
includeRepliesboolfalseReplies are discussion, not job posts
stateStoreNamestringhacker-news-api-stateThe monitor's memory
firstRunBehaviorenumemit-allOr baseline-only (free)
lookbackHoursint24Used when the monitor has no state yet
maxItemsint2000 means everything, up to the 5,000-row run ceiling
includeTextbooltruetext and textHtml on stories and comments
includeSummarybooltrueThe free summary rows
maxConcurrencyint4Targets in flight
maxRunSecsint240Time budget, max 3600
proxyConfigurationobject{ "useApifyProxy": true }Datacenter. Both APIs answer it

Output reference

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

rowTypeChargedWhat it is
storyyesA story, a Show HN, an Ask HN or a Launch HN
commentyesOne comment, with depth, parentId and storyId
useryesOne profile
jobyesOne YC job post, or one parsed Who is hiring? posting
summarynoWhat the run did: hit counts, feed lengths, parse rates, monitor state
diagnosticnoAnything 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

summaryTypeCarries
run-summaryWhich 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-summarynbHits, rowsReturned, rowsFiltered, slices, truncated
feed-summaryfeedLength — how long the feed really is today
thread-summarycommentsReported vs commentsReturned, jobsParsed, jobsUnparsed, parseRate, confidenceHistogram
user-summarysubmissionCount and how many submissions came back
monitor-summaryfirstRun, 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

EventWhat it isFREEBRONZESILVERGOLD
actor-startCharged once per run, and only after the run has returned at least one item$0.001$0.001$0.001$0.001
itemOne 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.

RunCost
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.

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}'
# 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

News, documents & the web

Business, finance & jobs

Apps & games