⏱️ Reading time: 13 min

Hacker Times appeared a few days ago on Hacker News with an unusual format: it presents the site’s stories as a newspaper front page, with hourly editions and direct links to the original source. Behind that design there is no special agreement with Y Combinator or privileged data access: there is a public, free Hacker News API that requires no authentication and that anyone can query from their own terminal.

📑 En este artículo
  1. TL;DR
  2. What is the Hacker News API?
  3. Why it matters
  4. How it works under the hood
    1. Flow of a basic reader
  5. Practical examples
    1. Request sequence
  6. Getting started
  7. Real-world use cases
  8. Common mistakes and best practices
  9. Comparison with alternatives
  10. Going deeper
  11. Frequently Asked Questions
    1. Does the public Hacker News endpoint need an API key?
    2. How often does topstories.json update on Hacker News?
    3. Does the Hacker News REST interface support keyword search?
    4. What does it mean for a Hacker News item to have dead or deleted set to true?
    5. Is there a requests-per-second limit on the Hacker News API?
  12. References

Building something similar doesn’t require asking for permission or an access token. It’s enough to understand the available endpoints, how the data is structured, and what practical limits that interface has when you use it seriously.

TL;DR

  • The Hacker News API lives at hacker-news.firebaseio.com/v0/ and doesn’t ask for an API key or registration.
  • topstories.json, newstories.json, and beststories.json return arrays of IDs, never the full content.
  • Each item.json brings back an object with by, score, time, title, type, and url, or null if it was deleted.
  • Algolia replicates the same content at hn.algolia.com/api with full-text search.
  • A functional custom reader fits in under 30 lines of JavaScript using Node.js’s native fetch.

What is the Hacker News API?

The Hacker News API is the public, read-only interface that exposes every story, comment, and user from Y Combinator’s site in JSON. It runs on Firebase Realtime Database, requires no authentication or key, and lets you build readers or bots using the same data the original site uses.

The project is open source and maintained by the Hacker News team itself on GitHub. The full documentation, with every endpoint and the schema for each data type (story, comment, job, poll, pollopt), lives in the HackerNews/API repository.

Unlike many commercial APIs, there are no paid quotas or plans. It’s a resource the community maintains for the community, which is why a script firing thousands of requests per second ends up hurting every other client sharing the same backend.

The topstories.json endpoint returns up to 500 IDs, never the full content of each story. Foto de Mika Baumeister en Unsplash

Why it matters

Before this API, keeping up with what was happening on Hacker News meant opening the site and refreshing the tab by hand. Today there are alternative readers, Slack bots, browser extensions, and automated newsletters running on the same endpoints, without asking anyone for permission.

Hacker Times is a recent example: it takes the day’s stories and presents them as a newspaper edition, with its own front page and hourly updates. The project launched as a Show HN and, like any other client, only needs to make HTTP requests to the same public interface you’ll use in this article.

It also matters as an API design case study: it exposes raw data with no permission layers, delegates text search to a third party (Algolia) instead of building it in-house, and leaves presentation entirely up to the developer. That openness has a cost, though: with no keys or quotas, the only barrier against abuse is the good faith of whoever consumes the data, so caching and spacing out requests is left to each client, not the server.

How it works under the hood

The API runs on Firebase Realtime Database, the same Google service used by thousands of mobile apps to sync data. That explains why every URL ends in .json: it’s not a REST convention chosen on purpose, it’s literally how Firebase exposes any node in its data tree.

The endpoints split into two groups. The first are lists of IDs: topstories, newstories, beststories, askstories, showstories, and jobstories. Each one returns a flat array of integers, never the story’s content. The second group are individual items, at /v0/item/{id}.json, which bring back the full object for a story, comment, poll, poll option, or job posting, depending on the value of its type field.

Each item has a fixed set of fields depending on its type. A story carries by (user), score, time (Unix timestamp), title, url, and kids (the IDs of its top-level comments). A comment carries parent instead of url, and its own kids with nested replies. If you request an ID that’s deleted or flagged dead by moderation, the deleted or dead fields come back as true.

There’s a detail that breaks expectations the first time: requesting /v0/item/999999999.json, an ID that never existed, doesn’t return a 404 error. It returns null with a 200 status code. Your code has to filter out nulls before reading story.title, or the script crashes with a TypeError.

curl https://hacker-news.firebaseio.com/v0/maxitem.json

That command confirms the Hacker News API is alive: it returns an integer that grows with every new story or comment on the site, for example 45102873. If that number doesn’t change over several minutes of normal activity, the problem is in your network, not in Firebase.

Flow of a basic reader

flowchart TD
    A["Your script"] --> B["GET /v0/topstories.json"]
    B --> C["Array of up to 500 IDs"]
    C --> D["GET /v0/item/ID.json (for each ID)"]
    D --> E["Story object: title, score, by, url"]
    E --> F["Your reader or filter"]

Practical examples

The shortest possible example is requesting a single already-published story, to see the exact shape of the response:

curl https://hacker-news.firebaseio.com/v0/item/8863.json

That is, literally, the response documented in the official repository for ID 8863: the story where Drew Houston introduced Dropbox on Hacker News in 2007.

{
  "by": "dhouston",
  "id": 8863,
  "score": 111,
  "time": 1175714200,
  "title": "My YC app: Dropbox - Throw away your USB drive",
  "type": "story",
  "url": "http://www.getdropbox.com/u/2/screencast.html"
}

The format hasn’t changed in almost twenty years. The second step is fetching a full front-page list. Using Node.js’s native fetch, included since version 18 with nothing to install:

const res = await fetch("https://hacker-news.firebaseio.com/v0/topstories.json");
const ids = await res.json();
console.log(ids.slice(0, 5));

That prints an array of five integers, for example [41234567, 41234321, 41233890, 41233654, 41233112]. The values keep changing because the list reorders whenever a story moves up or down the ranking, but the shape, a flat array of numbers, is always the same.

The third step combines both calls to build something useful: a function that fetches the N newest front-page stories and filters them by minimum score.

async function getTopStories(limit = 10, minScore = 100) {
  const idsRes = await fetch("https://hacker-news.firebaseio.com/v0/topstories.json");
  const ids = (await idsRes.json()).slice(0, limit);

  const stories = await Promise.all(
    ids.map(id =>
      fetch(`https://hacker-news.firebaseio.com/v0/item/${id}.json`).then(r => r.json())
    )
  );

  return stories.filter(story => story && story.score >= minScore);
}

getTopStories().then(stories => {
  stories.forEach(s => console.log(`${s.score} pts - ${s.title}`));
});

Promise.all fires the N item.json requests in parallel instead of waiting for them one by one, which cuts total time from several seconds down to a few hundred milliseconds. The output, printed with forEach, looks like this (scores and titles change on every real run):

342 pts - Rust 1.90 is now available
218 pts - Show HN: I built my own Hacker News reader
155 pts - A deep dive into io_uring
💡 Tip: caching the result of topstories.json in memory for 60 seconds avoids hitting the API on every render of your interface without losing real freshness.

Adding a keyword filter, to build something like a topic alert, is just one more function layered on top of the previous one:

function filterByKeyword(stories, keyword) {
  return stories.filter(s =>
    s.title && s.title.toLowerCase().includes(keyword.toLowerCase())
  );
}

Request sequence

sequenceDiagram
    participant C as Client
    participant F as Firebase HN API
    C->>F: GET /v0/topstories.json
    F-->>C: array of up to 500 ids
    loop for each id
        C->>F: GET /v0/item/id.json
        F-->>C: story object or null
    end
    Note over C,F: no authentication, HTTPS only
Node.js has included fetch natively since version 18, with no extra packages to install. Foto de Julio Lopez en Unsplash

Getting started

Requirements: Node.js 18 or newer and an internet connection, no account or token of any kind.

  1. Install Node on Linux or macOS with nvm (nvm install 22) or your distribution’s package manager; on Windows, winget install OpenJS.NodeJS.LTS does the same thing in a single command.
  2. Check the version with node -v: it should print v18 or newer, for example v22.11.0.
  3. Create a folder and a reader.js file.
  4. Paste in the getTopStories function from the earlier example along with the block that runs it.
  5. Run node reader.js from the terminal.
  6. You should see a list of lines with score and title, one for each story that passed the minimum score filter.

Real-world use cases

  • Alternative readers with their own design: Hacker Times is the example that opened this article, with a newspaper format and hourly editions, but the same pattern works for a minimalist terminal reader.
  • Full-text search: hn.algolia.com is, in itself, a reader built by Algolia on top of the same data, with a search index the official API doesn’t offer.
  • Chat bots: scripts that post to Slack, Discord, or Telegram the stories that pass a certain score as soon as they show up in topstories.
  • Trend dashboards: accumulating topstories every hour to chart which topics, like AI or security, dominate the week.
  • Personal archives: saving stories with more than X points to a local database, to keep your own historical archive without depending on the site.

Common mistakes and best practices

  • Not filtering nulls: item.json can return null; accessing .title on null throws a TypeError.
  • Ignoring dead and deleted: showing them anyway can confuse the end user; the normal approach is to hide them or mark them as removed.
  • Requesting items in series: a for loop with sequential await multiplies response time by N compared to Promise.all.
  • Assuming stable order: topstories.json changes constantly, so to compare positions you need to track by ID, not by array index.
  • Not caching: requesting the same item.json multiple times within seconds wastes requests; a few minutes of caching is enough for a personal reader.
  • Aggressive polling: there’s no documented rate limit, but that’s not an invitation to hit the server every 100 ms; 30 to 60 seconds is plenty for any human-facing reader.
⚠️ Watch out: a nonexistent ID responds with 200 and a null body, not a 404. Explicitly checking for this case keeps your reader from crashing entirely over a single deleted story.

Comparison with alternatives

The official API isn’t the only door into Hacker News data. Choosing between it, Algolia, and scraping depends on whether you need search, how real-time you want the data, and how much maintenance you’re willing to take on.

OptionWhen to use itAdvantageLimitation
Official API (Firebase)Basic readers, bots, dashboardsFree, no auth, near real-time dataNo text search; one request per item
Algolia HN Search APISearch by keyword or date rangeFull-text search with filters by date and typeNot maintained directly by YC, but by Algolia
HTML scrapingData no endpoint exposes (exact visual order)Access to any data visible on the pageBreaks with every site redesign

Going deeper

Everything above uses polling: you request data, wait for the response, repeat every N seconds. But since the backend is Firebase Realtime Database, there’s an alternative that pushes changes instead of the client pulling them: subscribing directly with the Firebase SDK.

The Hacker News API’s own repository documents this path: instead of making REST requests, the Firebase client connects to the project and uses .on('value', callback) on a node like v0/item/{id}, so the callback only fires when that node changes, without the code having to ask again.

To detect changes without subscribing node by node, there’s also /v0/updates.json, which returns two arrays: items (IDs of stories or comments that changed recently) and profiles (users who updated their profile). To confirm it really reflects recent activity, request it twice a minute apart: under normal traffic, the items array should bring back different IDs on each run.

The trade-off of using the Firebase SDK instead of REST is adding one more dependency and a persistent connection model, instead of simple stateless HTTP requests. For a simple bot or a script that runs once a day, fetch is enough; for a dashboard that needs to reflect live changes, subscribing avoids thousands of repeated requests.

stateDiagram-v2
    [*] --> Polling
    Polling --> Polling : GET every N seconds
    Polling --> Stale : long interval
    [*] --> Subscription
    Subscription --> UpToDate : automatic push via Firebase SDK

Your next step: clone the getTopStories function from this article, modify it to save stories with a score higher than 300 to a local JSON file, and schedule it with cron to run once an hour.

📬 Get new articles by email

We only email about big articles (1-2 a month).

Frequently Asked Questions

Does the public Hacker News endpoint need an API key?

No. It’s read-only, public, and requires no registration, token, or authentication header: any GET request to hacker-news.firebaseio.com/v0/ works directly from a browser or terminal.

How often does topstories.json update on Hacker News?

There’s no documented fixed interval: the array reorders every time the site’s ranking algorithm recalculates positions, which during high-traffic hours can happen every few minutes.

Not natively. For full-text search by date or author, the community project Algolia exposes the same data indexed at hn.algolia.com/api.

What does it mean for a Hacker News item to have dead or deleted set to true?

dead means the story or comment was removed by moderation but its ID still exists; deleted means the author removed it themselves. In both cases item.json can return the object with those flags.

Is there a requests-per-second limit on the Hacker News API?

There’s no publicly documented rate limit, but since it’s a free service maintained by the community, the right approach is to cache results and space out requests: abusing the frequency hurts every other client sharing the same backend.

References

📱 Like this content? Follow @programacion on Telegram for daily tech content in Spanish: quick summaries, fresh content every day. @programacion

Featured image: Foto de Mika Baumeister en Unsplash

Did it work for you? Got a different error? Say so below: questions get answered and help the next reader.

Leave a comment
Categories: SecurityTutorials

Clara Vásquez

Cybersecurity analyst focused on critical vulnerabilities, zero-days, and emerging threats. Covers high-impact CVEs, malware analysis, ransomware incidents, and security trends with a LATAM lens.

0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *

You can include code inside <code>…</code> or, for several lines, <pre><code>…</code></pre>.

This site uses Akismet to reduce spam. Learn how your comment data is processed.