# Hacker News Search — momentum, not just all-time points (`entrogix_works/hacker-news-search`) Actor

Search Hacker News stories, Show HN and Ask HN through the public search API. Each row carries points, comments, comments-per-point and — for stories still inside their 48-hour scoring window — points-per-hour, so a story climbing now does not lose to a 2020 post. Unofficial.

- **URL**: https://apify.com/entrogix\_works/hacker-news-search.md
- **Developed by:** [Entrogix Works](https://apify.com/entrogix_works) (community)
- **Categories:** News, Social media
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.00 / 1,000 story scrapeds

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?

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 Search — momentum, not just all-time points

Search Hacker News through its public search API and get one row per story, with the fields
you need to tell a post that is climbing right now from a post that was big in 2020.

### What the point count hides

Search sorted by relevance puts old winners on top. The first result for `rust` is a 2020 story
with 1,582 points. That number is real, and it is also useless if what you want is
"what is getting traction today".

Every row therefore carries:

- **`pointsPerHour`** — points divided by hours since posting, **but only for stories still inside
  the 48-hour scoring window.** Past that, HN scores stop moving, and dividing a frozen score by an
  ever-growing age produces a number that shrinks forever. Older rows get `null`, not a small number.
  The run summary tells you how many rows were inside the window, so a single high-momentum row
  is never mistaken for the only story worth reading.
- **`commentsPerPoint`** — separates a link everyone upvoted and nobody discussed from a thread
  that argued for a day.
- **`domain`** — the publisher, already normalised (`www.` stripped), so you can group by source.

### Fields

| Field | Notes |
|---|---|
| `storyId`, `hnUrl` | The HN item and its discussion page |
| `kind` | `story`, `show_hn`, `ask_hn`, `poll` or `job` |
| `title`, `url`, `domain` | `url` and `domain` are **`null`** for Ask HN and text posts — that is "no external link", not a scrape failure |
| `author` | The HN handle only. No profile, no name, no email |
| `points`, `comments`, `commentsPerPoint` | As of the moment of the run |
| `pointsPerHour`, `ageHours` | `pointsPerHour` is `null` beyond 48 hours (see above) |
| `createdAt` | ISO 8601, UTC |
| `sameUrlSubmissions`, `bestPointsForUrl`, `isBestForUrl` | See the limit below |

#### The one field with a hard limit

`sameUrlSubmissions` counts how many times the same link appears **in this run's results**,
after stripping tracking parameters (`utm_*`, `ref`, `fbclid`). It is **not** how many times the
link was ever submitted to Hacker News. Measured over 500 results per query on 2026-08-23,
8 rows matched for `openai`, 6 for `chatgpt` and 37 for `hacker news`, with a maximum of 4
submissions for any one link. Useful as a flag, not as a headline. If you need the true
resubmission history of one URL, search for that URL as the query.

### Input

```json
{
  "query": "rust",
  "tags": "story",
  "sort": "date",
  "sinceDays": 7,
  "minPoints": 20,
  "maxItems": 200
}
```

- `sort: "date"` is what you want for monitoring. `relevance` favours well known posts.
- `sinceDays` matters more than it looks. Without it, search reaches back to 2006.
- `minPoints` and `minComments` are applied by the API before results are fetched,
  so filtered-out stories are never charged for.
- `tags: "front_page"` returns the front page **at the moment the run starts**. It is a snapshot;
  run it on a schedule if you want a history.
- Leave `query` empty only when you set a `tags` slice — an empty query with no tag is rejected
  rather than returning the whole site.

### Output

One dataset row per story, plus a `SEARCH_SUMMARY` record with the total number of matches on
Hacker News (which is almost always far larger than what you asked for), how many rows fell inside
the scoring window, the median score, and the ten most frequent domains.

### Pacing

Requests are serialised with a minimum interval. Please do not lower `minIntervalMs`.
The search API is offered free and without an API key; keeping the load light is the reason it
stays that way.

Unofficial. Not affiliated with Y Combinator or Hacker News.

# Actor input Schema

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

Words to match in the title, URL or text. Leave empty to pull a whole slice by tag, for example the front page.

## `tags` (type: `string`):

Front page returns what is on the front page at the moment the run starts, so it is a snapshot, not a history.

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

Relevance is the search default and favours well known posts. Newest first is what you want for monitoring.

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

Optional. Drops everything below this score before it is fetched, so you are not charged for it.

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

Optional. Same as minimum points, but on the comment count.

## `sinceDays` (type: `integer`):

Optional. Without it, search reaches back to 2006 and old high-scoring posts dominate the results.

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

You are charged per story returned. The API serves up to 1000 per request.

## `minIntervalMs` (type: `integer`):

Pacing between page fetches. Do not lower this.

## `useProxy` (type: `boolean`):

Off by default. The API is served without a proxy; turn this on only if you see rate limiting.

## Actor input object example

```json
{
  "query": "rust",
  "tags": "story",
  "sort": "relevance",
  "maxItems": 100,
  "minIntervalMs": 2000,
  "useProxy": false
}
```

# Actor output Schema

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

Every story this run produced, as JSON.

## `summary` (type: `string`):

How many stories matched in total, how many were returned, how many are still inside the scoring window, and which domains came up most.

# 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": "rust"
};

// Run the Actor and wait for it to finish
const run = await client.actor("entrogix_works/hacker-news-search").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": "rust" }

# Run the Actor and wait for it to finish
run = client.actor("entrogix_works/hacker-news-search").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": "rust"
}' |
apify call entrogix_works/hacker-news-search --silent --output-dataset

```

## MCP server setup

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

```

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/EnlIvz4m2ordEnYoF/builds/DF3ARWQINS8E0Akba/openapi.json
