DEV Community

Kevan
Kevan

Posted on

Is anyone still reading that thread? Check the author's last reply with 3 free APIs

When you answer someone's question on a forum, the date of the post tells you less than you think.

A founder told me their replies stopped working after day 5 to 7. By then the person has picked a tool or moved on, and your answer lands in an empty room.

A better signal: did the author reply to anyone in their own thread in the last 24 hours? If yes, they're still reading. Your answer gets seen today.

Three platforms give you that for free, without a key: DEV, Hacker News and Discourse forums. Here's the whole thing, about 40 lines of Node 18+.

The shared part
Every platform returns replies as a tree. Normalize each one to { author, at, children }, then walk it:

// Latest reply written by author anywhere in the tree (ms), or null.
function lastReplyBy(nodes, author) {
let last = null;
const walk = (items) => {
for (const n of items) {
if (n.author?.toLowerCase() === author.toLowerCase() && n.at > (last ?? 0)) last = n.at;
walk(n.children);
}
};
walk(nodes);
return last;
}

const isAlive = (at, hours = 24) => at !== null && Date.now() - at <= hours * 3_600_000;
DEV
The article endpoint gives you the id, the comments endpoint gives you the full tree:

async function devto(user, slug) {
const article = await (await fetch(https://dev.to/api/articles/${user}/${slug})).json();
const raw = await (await fetch(https://dev.to/api/comments?a_id=${article.id})).json();
const map = (cs) =>
cs.map((c) => ({ author: c.user?.username, at: Date.parse(c.created_at), children: map(c.children ?? []) }));
return lastReplyBy(map(raw), user);
}
Hacker News
The Algolia API returns the whole thread in one call. Times are in seconds, and deleted comments have no author (the ?. above handles that):

async function hn(id) {
const item = await (await fetch(https://hn.algolia.com/api/v1/items/${id})).json();
const map = (cs) =>
cs.map((c) => ({ author: c.author, at: c.created_at_i * 1000, children: map(c.children ?? []) }));
return lastReplyBy(map(item.children ?? []), item.author);
}
Discourse forums
A lot of product communities run on Discourse. Add .json to a topic URL and you get a flat list where the first post is the question itself:

async function discourse(host, topicId) {
const t = await (await fetch(https://${host}/t/${topicId}.json)).json();
const posts = t.post_stream.posts;
const replies = posts.slice(1).map((p) => ({ author: p.username, at: Date.parse(p.created_at), children: [] }));
return lastReplyBy(replies, posts[0].username);
}
One limit: on long topics, that JSON only includes the first batch of posts (20 by default).

Using it
const at = await hn(50131997);
console.log(isAlive(at) ? "author still replying" : "probably moved on");
Reddit has the same kind of tree in .json, but it often blocks requests coming from servers, so I left it out.

Why I built this
I'm building FounderPulse, which finds people who just posted about the problem your product solves. A post asking for exactly what you sell is worth a lot more when its author is still in the thread, so live threads now move to the top of the list. If you want to see what it finds for your product, there's a free test at founder-pulse.com, no signup.

What signal do you use to decide a thread is still worth answering?

Top comments (0)