Can Your Agent Trust What It Reads on Reddit?

Upvotes measure agreement, not accuracy. Four checks — recency, account history, dissent, and cross-source — that turn a thread into evidence instead of a vibe.

Profile picture of Serply
Serply
A highly upvoted comment being checked against its author's posting history

An agent that reads Reddit and reports what it found is doing something subtly dishonest. It’s presenting “the top comment said X” as if it were “X is true,” and those are very different claims.

Sometimes they coincide. Often enough they don’t, and the ways they come apart are predictable enough to design against.

This post covers the four failure modes that actually matter, and what each one costs to check.

What upvotes actually measure

Start here, because most bad Reddit grounding traces back to misreading this one signal.

A high score means a lot of people who saw the comment agreed with it, or found it funny, or found it satisfying. It does not mean it’s correct. On questions with an emotionally appealing wrong answer, the vote system reliably promotes the wrong answer — that’s not a flaw in Reddit, it’s what a popularity mechanism does.

Score is genuinely useful as a relevance filter. It’s much weaker as a truth filter. The practical consequence: use score to decide what to read, not to decide what to believe.

Failure mode 1: the advice is old

This is the most common one and the easiest to fix. A thread from 2021 explaining how to configure a tool is often confidently, specifically wrong now, and nothing in the text says so. The model reads authoritative-sounding instructions and repeats them.

Reddit’s raw payload carries created_utc on every post and comment, so the fix is a filter:

import time

MAX_AGE_DAYS = {
    "default": 1095,   # 3 years — durable topics
    "software": 365,   # APIs, frameworks, tooling
    "pricing": 180,    # anything with a number attached
}


def fresh_enough(item: dict, topic: str = "default") -> bool:
    age_days = (time.time() - item["created_utc"]) / 86400
    return age_days <= MAX_AGE_DAYS.get(topic, MAX_AGE_DAYS["default"])

Better than filtering, though, is labelling. Drop the age into the text you hand the model:

def with_age(item: dict) -> str:
    age_days = int((time.time() - item["created_utc"]) / 86400)
    if age_days > 730:
        stamp = f"[{age_days // 365}y old — may be outdated]"
    else:
        stamp = f"[{age_days}d old]"
    return f"{stamp} {item['body']}"

A model that can see “[4y old — may be outdated]” next to a claim will hedge appropriately. A model handed the same text with no timestamp has no way to know, and won’t.

Constraining at the request layer helps too — sort=top with t=year or t=month on a subreddit listing never surfaces the 2019 threads at all.

Failure mode 2: the account is not a person

Coordinated promotion on Reddit is real and getting better at hiding. Reddit reports blocking 23 million spam views and revoking nearly 2 million inauthentic votes per day, and the campaigns that get through increasingly use AI-generated profiles with plausible bios and consistent posting history.

You are not going to catch a well-run operation. You can catch the cheap ones, and the cheap ones are the majority.

The check is the user endpoint, and it costs one request:

import os
import httpx
from collections import Counter

HEADERS = {"X-Api-Key": os.environ["SERPLY_API_KEY"]}


def author_profile(username: str) -> dict:
    r = httpx.get(
        f"https://api.serply.io/v1/reddit/user/{username}",
        params={"limit": 100, "sort": "new"},
        headers=HEADERS,
        timeout=15.0,
    )
    r.raise_for_status()
    items = [c["data"] for c in r.json()["data"]["children"]]

    subs = Counter(i.get("subreddit", "?") for i in items)
    return {
        "items": len(items),
        "distinct_subs": len(subs),
        "top_sub_share": subs.most_common(1)[0][1] / len(items) if items else 0,
    }


def looks_astroturfed(profile: dict) -> bool:
    if profile["items"] < 5:
        return True                          # brand new or near-empty account
    if profile["distinct_subs"] <= 2:
        return True                          # single-purpose account
    if profile["top_sub_share"] > 0.9:
        return True                          # posts about one thing, ever
    return False

None of these are proof. A genuine expert might post only in their field. But when a single comment is carrying most of the weight in your agent’s answer, “the author has five comments and they’re all in one subreddit praising one product” is worth knowing before you cite it.

Run this selectively. Checking every comment in a thread is wasteful; checking the two or three your answer actually depends on is cheap and catches the obvious cases.

Failure mode 3: you only read the agreement

The most valuable thing in a Reddit thread is usually the disagreement, and the most common pruning mistake is cutting it out. Filter to high-scoring comments only and you keep the consensus while discarding the specific, well-argued rebuttal sitting at +3 underneath it.

So preserve dissent deliberately:

def keep_dissent(children: list, top_n: int = 5) -> list:
    """Keep the top comments, plus the highest-scored direct reply to each."""
    kept = []
    for child in children[:top_n]:
        if child.get("kind") != "t1":
            continue
        parent = child["data"]
        kept.append((0, parent))

        replies = parent.get("replies")
        if isinstance(replies, dict):
            direct = [
                c["data"] for c in replies["data"]["children"]
                if c.get("kind") == "t1"
            ]
            if direct:
                best = max(direct, key=lambda c: c.get("score", 0))
                kept.append((1, best))
    return kept

Keeping the best reply to each top comment costs almost nothing in tokens and changes the character of what the model sees. A claim with its strongest counterargument attached is evidence. A claim on its own is an assertion.

Then say so in the prompt:

The following are Reddit comments with scores and ages. Score reflects
agreement, not accuracy. Where comments disagree, report the disagreement
rather than picking a side. If a claim is contradicted by a reply, say so.

Failure mode 4: nothing checked it against the world

The last defense is not a Reddit technique at all. Claims that matter get verified against a second source.

The routing rule that works: verifiable facts go to search, subjective verdicts stay on Reddit.

def route(claim_type: str) -> str:
    factual = {"price", "spec", "release_date", "version", "company", "policy"}
    return "verify_via_search" if claim_type in factual else "reddit_only"

If a thread says a plan costs $40/month, that’s checkable — go check it. If a thread says the plan isn’t worth $40/month, that’s the opinion you came for, and there’s no page anywhere that verifies it.

This is the same discipline as any citation-checking pass, and it’s worth measuring whether your agent is actually using its sources rather than assuming it is.

What to tell the user

The output difference is mostly about attribution. Compare:

The consensus is that the build quality declined after 2024.

against:

Several r/hardware threads from the past six months report build-quality problems after 2024, with the most-upvoted comment (+840) citing hinge failures. One well-received reply (+120) argues this is limited to a single production batch. I couldn’t verify either claim against the manufacturer.

The second is longer and considerably more honest. It tells the user what kind of evidence this is, which lets them weight it themselves.

The cost of doing this properly

Adding up: one listing request to find threads, two or three comment requests to read them, and one or two user requests to check the authors carrying the most weight. Call it six requests for a well-grounded answer, at $1.50 per 1,000 on the Starter Pack and $0.75 at volume — under a cent, with responses cached for ten minutes and cached responses free.

The verification layer is not where the money goes. It’s where the credibility comes from, and it’s cheap enough that skipping it is hard to justify.

Reddit is a good source. It’s just a source that requires reading like a human would — checking when it was written, who wrote it, and whether anyone credible disagreed. Your agent should do the same. 2,500 free credits is enough to build the whole checking layer and see what it catches.