DEV Community

Cover image for Three public HTTP APIs I read daily without registering for a key
MORINAGA
MORINAGA

Posted on

Three public HTTP APIs I read daily without registering for a key

My daily trends fetch is a Node.js script that runs in GitHub Actions, hits three APIs, and writes a JSON file that feeds my X-drafts pipeline later in the day. None of the three sources require an API key. No OAuth flow, no dashboard signup, no rate-limit token to rotate.

That sounds trivial, but it matters for CI pipelines. Every API key stored in GitHub Secrets is a secret that can expire, a secret that has to be rotated when a team member leaves, a secret that creates a failure surface. Keyless reads eliminate all of that for the sources where it's possible.

Here are the three I use and the practical limits of each.

Hacker News Firebase API

The HN API is publicly documented on GitHub and hosted on Firebase. No key, no auth header, no rate limit published in the official docs.

const ids = await fetchJSON(
  "https://hacker-news.firebaseio.com/v0/topstories.json"
);
const items = await Promise.all(
  ids.slice(0, 20).map((id) =>
    fetchJSON(`https://hacker-news.firebaseio.com/v0/item/${id}.json`)
  )
);
Enter fullscreen mode Exit fullscreen mode

This returns the top story IDs, then fetches each item. The item objects include title, url, score, and descendants (comment count). For my use case — grabbing the top 20 stories to identify what's trending in dev — that's everything I need.

The practical limit is latency. Fetching 20 items individually over Firebase takes 2–4 seconds depending on cold-start behavior. Parallelizing with Promise.all handles this fine in a script context. In a browser context, sequential fetches would be painful.

Firebase occasionally throttles aggressively for a few minutes if you hit it from many IPs in a short window. In four months of daily runs from GitHub Actions I've hit this once. The fix was a 500ms delay between item fetches, which is enough to avoid triggering it in practice.

dev.to public API

dev.to exposes a read-only articles endpoint that works without authentication for public content. The top=1 parameter returns articles sorted by recent reaction count.

const items = await fetchJSON(
  "https://dev.to/api/articles?per_page=12&top=1"
).catch(() => []);
Enter fullscreen mode Exit fullscreen mode

The response includes title, url, public_reactions_count, comments_count, and tag_list on each article. The catch(() => []) is load-bearing: dev.to's API returns 503 occasionally, and swallowing that to an empty array lets the rest of the trends fetch continue.

One thing that surprised me: top=1 doesn't mean "top from the last 1 day". The documentation is ambiguous about the time window. In practice, articles from the previous two to three days appear in the results. For trending detection that's fine; for building a "published today" feed it would be wrong.

The API does not require auth for reading public articles. It has a rate limit — 10 requests per second per IP, per the docs — which I've never come close to hitting with a once-daily run.

Reddit .json endpoints

Reddit's most underused feature is the .json suffix that works on almost any listing URL. Append .json to a subreddit URL and you get the full listing data without OAuth.

const j = await fetchJSON(
  "https://www.reddit.com/r/programming/top.json?t=day&limit=10"
).catch(() => null);
Enter fullscreen mode Exit fullscreen mode

The response structure is {data: {children: [{data: {title, url, score, ...}}]}}. Each child has title, url, score, num_comments, author, and selftext (body for self-posts).

The User-Agent header matters here. Reddit blocks requests without a user agent string, and returns 429 for user agents that look like bots making too many rapid requests. I use my-app/1.0 as the UA string; that's been stable across months of daily runs.

const r = await fetch(url, { headers: { "user-agent": "my-app/1.0" } });
Enter fullscreen mode Exit fullscreen mode

Reddit's .json endpoint does not require an API key for public subreddit content. Accessing private subreddits or performing writes requires OAuth. For the reading use case — grabbing the top posts from r/programming and r/SideProject — no registration is needed.

The limit I hit in practice: the t=day (top of the day) filter doesn't always return 10 results early in UTC because the day is new and few posts have accumulated significant score yet. Running at 06:00 UTC I sometimes get 3–4 results instead of 10. Running at 22:00 UTC I consistently get the full batch.

What I do with the combined output

The script writes a single JSON file to content/trends/YYYY-MM-DD.json with all three sources merged:

const all = [
  ...hn.map((x) => ({ ...x, source: "HN" })),
  ...devto.map((x) => ({ ...x, source: "dev.to" })),
  ...reddit.map((x) => ({ ...x, source: `r/${sub}` })),
];
await writeFile(OUT, JSON.stringify({ date: today, items: all }, null, 2));
Enter fullscreen mode Exit fullscreen mode

A later step in the same day's workflow reads this file and uses it to draft X posts via Claude. The trends file is the handoff artifact between the data-collection step (no API key, runs first) and the generation step (requires Anthropic API key, runs second).

That separation also means I can test the collection step in isolation without spending any API budget. The trends fetch fails silently on individual source errors — Reddit 503, HN throttle, dev.to rate limit — and still writes a file with whatever it managed to collect. Downstream steps get degraded input rather than a broken run.

Where the keyless approach breaks down

Not every useful data source has a public read endpoint. GitHub's search API requires auth past 60 requests per hour. HuggingFace's models API requires a token for private repos but is keyless for public models — I use the keyless endpoint for that too, but it's limited to what HF considers "public". Steam's storefront API has undocumented endpoints that work without auth, though the behaviors are inconsistent enough that I treat them carefully.

The three sources above — HN, dev.to, Reddit .json — have been stable for the four months I've used them. That's not a guarantee of permanence. Reddit has changed its API policies before; dev.to's read rate limits could tighten. The fallback behavior (catch to empty array) means the pipeline degrades gracefully rather than breaking when that happens.

Part of an ongoing 6-month experiment running three AI-curated directory sites. The technical claims here are real; this article was AI-assisted.

Top comments (0)