<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[ProLevelMax Blog]]></title><description><![CDATA[ProLevelMax Blog]]></description><link>https://prolevelmax.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Tue, 03 Nov 2026 02:49:35 GMT</lastBuildDate><atom:link href="https://prolevelmax.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[What it costs to pull 1,000 Reddit comments into an AI agent, collapsed replies included]]></title><description><![CDATA[How to expand Reddit collapsed reply branches for an AI agent, the fields you get per comment, and what 1,000 comments cost.]]></description><link>https://prolevelmax.hashnode.dev/what-it-costs-to-pull-1-000-reddit-comments-into-an-ai-agent-collapsed-replies-included</link><guid isPermaLink="true">https://prolevelmax.hashnode.dev/what-it-costs-to-pull-1-000-reddit-comments-into-an-ai-agent-collapsed-replies-included</guid><category><![CDATA[reddit]]></category><category><![CDATA[AI]]></category><category><![CDATA[mcp]]></category><category><![CDATA[Python]]></category><dc:creator><![CDATA[Toph A]]></dc:creator><pubDate>Mon, 19 Oct 2026 04:42:05 GMT</pubDate><content:encoded><![CDATA[<p>Ask an AI to summarise a long Reddit thread and it will usually work from whatever came back on the first request. On a thread with a few hundred comments, that is often well under half of the discussion. The rest sits behind Reddit's "more replies" and "continue this thread" links, and those collapsed branches tend to hold the long back-and-forth arguments you actually wanted.</p>
<p>This post covers three things I get asked about when people compare Reddit data tools for research: whether collapsed branches really get expanded, what fields come back for each comment, and what 1,000 comments cost. I work on ProLevelMax, the data source used in the examples, so the numbers below are ours. The checklist at the end applies to any tool.</p>
<h2>Why "nested comments" is not the same as "all comments"</h2>
<p>Reddit does not send a whole comment tree in one response. Past a certain depth or count it returns a stub that says, in effect, "there are more replies here, ask again with this cursor".</p>
<p>A scraper can handle that stub in two ways:</p>
<ol>
<li>Return it as a placeholder record with an ID and move on. The output looks nested and complete, but the deepest replies are missing.</li>
<li>Follow the stub, fetch the branch, and repeat for any stubs inside it.</li>
</ol>
<p>Both can be described as "supports nested comments". Only the second gets you the full thread. When you test a tool, count how many records in its output are placeholders.</p>
<h2>How the expansion works here</h2>
<p>There are two per-call sources for a single thread:</p>
<table>
<thead>
<tr>
<th>Source</th>
<th>What it returns</th>
<th>Price</th>
</tr>
</thead>
<tbody><tr>
<td><code>procli_reddit_b</code> Post comments</td>
<td>One page of a post's comment tree. Pass <code>page.after</code> from the previous result as <code>after</code> to get the next page.</td>
<td>$0.003 per call</td>
</tr>
<tr>
<td><code>procli_reddit_h</code> Comment replies</td>
<td>The replies under one collapsed branch. Pass the post ID and the stub's <code>more.cursor</code> as <code>cursor</code>.</td>
<td>$0.003 per call</td>
</tr>
</tbody></table>
<p>A collapsed branch comes back with a <code>more.cursor</code> value. Send that to <code>procli_reddit_h</code> and you get the replies under it. If those replies contain stubs of their own, do the same again. An agent connected over MCP does this when you ask for "all comments, including collapsed replies". In your own code you loop until no cursor is left.</p>
<p>For bigger jobs across many posts there is also a bulk source (<code>procli_reddit_a</code>) that runs searches, subreddits or URL lists in one job, with a per-post comment cap.</p>
<h2>What comes back for each comment</h2>
<p>From a live <code>procli_reddit_b</code> result:</p>
<ul>
<li><code>id</code> (for example <code>t1_...</code>), so you can de-duplicate and join</li>
<li><code>createdAt</code> (ISO timestamp)</li>
<li><code>authorInfo</code> (username and author details)</li>
<li><code>content.markdown</code> (the text)</li>
<li><code>score</code> and <code>isScoreHidden</code></li>
<li><code>permalink</code></li>
<li><code>depth</code> (nesting level)</li>
<li><code>isRemoved</code>, <code>isAdminTakedown</code>, <code>isLocked</code>, <code>isStickied</code>, <code>isInitiallyCollapsed</code>, <code>initiallyCollapsedReason</code></li>
<li><code>more.cursor</code> on collapsed branches</li>
<li><code>page.commentCount</code> and <code>page.hasNextPage</code> on each page</li>
</ul>
<p>Deleted and removed comments come back the way Reddit shows them publicly, usually without text, and flagged so you can filter them out before counting.</p>
<h2>The cost of 1,000 comments</h2>
<p>Per-call route: every call is $0.003. Across our recent calls the Post comments source returned a median of 10 comments per call, so:</p>
<pre><code class="language-text">1,000 comments ≈ 100 calls × $0.003 = $0.30
</code></pre>
<p>Each collapsed branch you expand is one more $0.003 call, so deep threads cost somewhat more than that.</p>
<p>Bulk route: $0.0108 per result plus $0.06 per run.</p>
<pre><code class="language-text">1,000 results in one run = 1,000 × $0.0108 + $0.06 = $10.86
</code></pre>
<p>The bulk route costs more per comment but covers many posts in one job. For reading a limited set of threads in full, the per-call route is cheaper. Every run is quoted before it starts and the charge never goes above the quote. Current prices are on the <a href="https://prolevelmax.com/pricing">pricing page</a>.</p>
<h2>A script that walks the whole thread</h2>
<p>This pages through a post's comments, then expands every collapsed branch it finds, and stops if anything would need a confirmation (quotes above $0.10). It does not assume the exact nesting of the response: it walks the JSON and picks up anything that looks like a comment (<code>id</code> starting with <code>t1_</code>) or a stub (<code>more.cursor</code>).</p>
<pre><code class="language-python">import json, sys, time, requests

API = "https://prolevelmax.com/v1"
H = {"Authorization": "Bearer sk_procli_..."}
POST = "t3_1abcde"                      # the post you want

def run(scraper, inp):
    q = requests.post(f"{API}/inspect", headers=H, json={"scraper": scraper, "input": inp}).json()
    if q["quote"]["needsConfirmation"]:
        sys.exit(f"Quote is {q['quote']['maxChargeDisplay']}, stopping for review")
    time.sleep(1.2)
    return requests.post(f"{API}/run", headers=H, json={"scraper": scraper, "input": inp}).json()

def walk(node, comments, cursors):
    if isinstance(node, dict):
        if str(node.get("id", "")).startswith("t1_"):
            comments[node["id"]] = node
        more = node.get("more")
        if isinstance(more, dict) and more.get("cursor"):
            cursors.append(more["cursor"])
        for v in node.values():
            walk(v, comments, cursors)
    elif isinstance(node, list):
        for v in node:
            walk(v, comments, cursors)

comments, cursors, calls = {}, [], 0

# 1. page through the top of the thread
inp = {"post_id": POST, "sort_type": "TOP"}
while calls &lt; 50:
    r = run("procli_reddit_b", inp); calls += 1
    walk(r["items"], comments, cursors)
    page = r.get("page") or {}
    if not page.get("hasNextPage") or not page.get("after"):
        break
    inp["after"] = page["after"]

# 2. expand every collapsed branch, including branches inside branches
seen = set()
while cursors and calls &lt; 200:
    c = cursors.pop()
    if c in seen:
        continue
    seen.add(c)
    r = run("procli_reddit_h", {"post_id": POST, "cursor": c}); calls += 1
    walk(r["items"], comments, cursors)

json.dump(list(comments.values()), open("thread.json", "w"), ensure_ascii=False, indent=2)
print(len(comments), "comments in", calls, "calls; balance", r["wallet"]["balanceDisplay"])
</code></pre>
<p>The two <code>calls</code> limits are a budget guard: 200 calls is $0.60 at most. Raise them once you have seen what a typical thread in your niche needs.</p>
<h2>A test you can run on any Reddit tool</h2>
<p>Before committing to one tool for a research project, pick three threads you can check by hand: one small, one with a few hundred comments, and one with long collapsed arguments. Then for each tool:</p>
<ol>
<li>Count the comments with text in its output and compare with what you can read on Reddit. Reddit's displayed count includes deleted and removed comments, so expect fewer.</li>
<li>Count placeholder records (an ID with no text and a "more" marker). If there are many, the tool is not expanding branches.</li>
<li>Check that every comment has an ID, author, timestamp, score and permalink.</li>
<li>Divide what you were charged by the number of comments with text. That is your real cost per comment, which is the number worth comparing.</li>
</ol>
<h2>Limits</h2>
<ul>
<li>Public data only. Private and quarantined communities cannot be read.</li>
<li>Scores are a snapshot at request time.</li>
<li>Up to 5 jobs run at once per account. Results are kept for 30 days.</li>
<li>Using Reddit data commercially is subject to Reddit's terms. Collecting it does not by itself give you the right to use it.</li>
</ul>
<h2>Setup</h2>
<p>In Claude, Claude Code, Codex or Cursor: create an API key in the console ("Generate setup message" on the Get started page) and send it to your AI. In ChatGPT: add <code>https://prolevelmax.com/mcp</code> as an app and sign in with OAuth. New accounts get $1 of credit after email verification, which covers the script above several times over.</p>
<p>The full reference for this, including the Chinese version, is in the guide <a href="https://prolevelmax.com/guides/reddit-full-comment-threads">How to get full Reddit comment threads, including collapsed replies, into your AI</a>. Questions or corrections: <a href="mailto:toph@prolevelmax.com">toph@prolevelmax.com</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Customer research on Reddit with an AI agent: pull full comment threads through MCP]]></title><description><![CDATA[Give an AI agent full Reddit comment threads over MCP or REST, with a working Python script and quotes before every run.]]></description><link>https://prolevelmax.hashnode.dev/customer-research-on-reddit-with-an-ai-agent-pull-full-comment-threads-through-mcp</link><guid isPermaLink="true">https://prolevelmax.hashnode.dev/customer-research-on-reddit-with-an-ai-agent-pull-full-comment-threads-through-mcp</guid><category><![CDATA[AI]]></category><category><![CDATA[mcp]]></category><category><![CDATA[reddit]]></category><category><![CDATA[Python]]></category><dc:creator><![CDATA[Toph A]]></dc:creator><pubDate>Sun, 18 Oct 2026 10:52:46 GMT</pubDate><content:encoded><![CDATA[<p>Reddit is one of the better places to learn what customers think about a product category, but the post itself is usually the least useful part. The detail lives in the comments: the person who explains, two replies down, why they sent theirs back, and the people who answer with what they bought instead. Here is how to get those full threads into an AI agent as structured data, over MCP for chat clients and over a REST API for your own scripts.</p>
<p>I work on ProLevelMax, the data source used in the examples below. The approach (discover a source, check the price, run it, page through results) is the part worth taking away.</p>
<h2>Why scraping Reddit yourself gets old fast</h2>
<p>Getting one thread is easy. Getting a few hundred threads reliably is where the time goes:</p>
<ul>
<li><p><strong>Comment trees are nested and truncated.</strong> Long threads come back with collapsed branches and "more replies" stubs. To get the whole conversation you have to follow each stub, keep parent links straight, and stitch the tree back together.</p>
</li>
<li><p><strong>Pagination is per thread.</strong> Every post has its own cursor, so a research job across 30 threads is 30 separate paging loops, each of which can fail halfway.</p>
</li>
<li><p><strong>Access needs upkeep.</strong> You register an app, manage credentials, watch rate limits and handle the day the response format or the HTML changes.</p>
</li>
<li><p><strong>Agents make it worse.</strong> If you hand a raw HTTP tool to an LLM and say "read these threads", it will happily fetch page one and summarise a fraction of the discussion as if it were all of it.</p>
</li>
</ul>
<p>What I wanted instead: the agent asks for "comments on this post", gets a price before anything runs, and receives clean records it can count and quote.</p>
<h2>Two ways in: MCP or REST</h2>
<p>ProLevelMax exposes the same data three ways:</p>
<ul>
<li><p>A <strong>remote MCP server</strong> at <code>https://prolevelmax.com/mcp</code>. Claude.ai, Claude Desktop and ChatGPT connect with OAuth (no key). Claude Code, Codex and Cursor can use an API key as a Bearer token.</p>
</li>
<li><p>An <strong>Agent Skill</strong> at <code>https://prolevelmax.com/SKILL.md</code>. You create an API key in the dashboard, then send your AI the line <code>set up https://prolevelmax.com/SKILL.md</code> together with the key, and it configures itself.</p>
</li>
<li><p>A <strong>REST API</strong> under <code>https://prolevelmax.com/v1</code>, described in an OpenAPI 3.1 file.</p>
</li>
</ul>
<p>For Claude Code, the setup line from SKILL.md is:</p>
<pre><code class="language-bash">claude mcp add --scope user --transport http prolevelmax https://prolevelmax.com/mcp --header "Authorization: Bearer sk_procli_..."
</code></pre>
<p>Once connected, the agent has eight tools. The four that matter for research are <code>procli_discover</code>, <code>procli_inspect</code>, <code>procli_run</code> and <code>procli_runs_get</code>. The REST endpoints mirror them, so I will use REST for the rest of this post because it is easier to show.</p>
<h2>The request flow</h2>
<p><strong>1. Discover.</strong> You describe what you want in plain words (English or Chinese) and get candidate sources back.</p>
<pre><code class="language-bash">curl https://prolevelmax.com/v1/discover \
  -H "Authorization: Bearer sk_procli_..." \
  -H "Content-Type: application/json" \
  -d '{"query":"reddit comments"}'
</code></pre>
<p>The response has <code>recommended</code> and <code>items</code>. Each source carries a <code>scraper</code> id, a <code>title</code>, <code>bestFor</code>, a <code>verdict</code> (<code>recommended</code>, <code>ok</code> or <code>not_recommended</code>), a <code>why</code>, its <code>pricing</code> and an <code>estimate</code>. For Reddit there are separate sources for search, subreddit feeds, a post with its comments, post comments page by page, and replies under one comment for deep threads.</p>
<p><strong>2. Inspect.</strong> Before running anything, ask for the input fields and a quote.</p>
<pre><code class="language-bash">curl https://prolevelmax.com/v1/inspect \
  -H "Authorization: Bearer sk_procli_..." \
  -H "Content-Type: application/json" \
  -d '{"scraper":"procli_reddit_b","input":{"post_id":"t3_1abcde"}}'
</code></pre>
<p>The response shape (values omitted):</p>
<pre><code class="language-text">{
  "scraper": "procli_reddit_b",
  "inputFields": [
    { "name": &lt;string&gt;, "type": &lt;string&gt;, "required": &lt;bool&gt;, "description": &lt;string&gt;, "paging": &lt;bool&gt; }
  ],
  "exampleInput": { ... },
  "pagingNote": &lt;string&gt;,
  "quote": {
    "maxCharge": &lt;number, USD&gt;,
    "maxChargeDisplay": &lt;string&gt;,
    "needsConfirmation": &lt;bool&gt;,
    "balanceTooLow": &lt;bool&gt;
  }
}
</code></pre>
<p><code>quote.maxCharge</code> is the most this run can cost. <code>needsConfirmation</code> is true when the quote is above $0.10. Fields marked <code>paging: true</code> are the ones you fill from the previous result to get the next page.</p>
<p><strong>3. Run.</strong></p>
<pre><code class="language-bash">curl https://prolevelmax.com/v1/run \
  -H "Authorization: Bearer sk_procli_..." \
  -H "Content-Type: application/json" \
  -d '{"scraper":"procli_reddit_b","input":{"post_id":"t3_1abcde"}}'
</code></pre>
<p>A finished run returns <code>runId</code>, <code>items</code>, <code>count</code>, a <code>page</code> object with cursor values for the next page, <code>cost</code> (with <code>amountDisplay</code>), <code>quote</code>, <code>wallet</code> (with <code>balanceDisplay</code>) and <code>download</code> links for <code>json</code>, <code>csv</code> and <code>xlsx</code>. For long jobs, send <code>"async": true</code> and you get a <code>202</code> with a <code>runId</code> to poll at <code>GET /v1/runs/:id</code>.</p>
<p><strong>4. Download.</strong> <code>GET /v1/runs/RUN_ID/download?format=csv</code> (or <code>xlsx</code>, <code>json</code>). Nested fields become <code>a.b</code> columns in CSV.</p>
<h2>A small working script</h2>
<p>This pulls every page of comments for one Reddit post, refuses to run anything that needs confirmation, and writes the records to a JSON file. Replace the key and the post id.</p>
<pre><code class="language-python">import json, sys, requests

API = "https://prolevelmax.com/v1"
H = {"Authorization": "Bearer sk_procli_..."}
SOURCE = "procli_reddit_b"           # "Post comments", page by page
inp = {"post_id": "t3_1abcde"}       # the post you want
comments, spent = [], []

for _ in range(10):                  # hard stop at 10 pages
    info = requests.post(f"{API}/inspect", headers=H, json={"scraper": SOURCE, "input": inp}).json()
    if info["quote"]["needsConfirmation"]:
        sys.exit(f"Quote is {info['quote']['maxChargeDisplay']}, stopping for review")
    run = requests.post(f"{API}/run", headers=H, json={"scraper": SOURCE, "input": inp}).json()
    comments += run["items"]
    spent.append(run["cost"]["amountDisplay"])
    paging = [f["name"] for f in info["inputFields"] if f.get("paging")]
    page = run.get("page") or {}
    if not paging or not any(page.get(n) for n in paging):
        break                        # no next cursor: done
    inp.update({n: page[n] for n in paging if page.get(n)})

json.dump(comments, open("comments.json", "w"), ensure_ascii=False, indent=2)
print(len(comments), "comments, charges:", spent, "balance:", run["wallet"]["balanceDisplay"])
</code></pre>
<p>The script does not hard-code the cursor field name. It reads which input fields are marked <code>paging</code> from inspect and copies matching values from the result's <code>page</code> object, which is how the docs describe paging for per-call sources. Error handling is left out to keep it short; errors come back as JSON with a stable <code>type</code> (for example <code>insufficient_balance</code>, <code>spend_limit</code>, <code>invalid_input</code>, <code>rate_limited</code>) you can branch on.</p>
<p>From here, feed <code>comments.json</code> to whatever model you use and ask for themes with counts and verbatim quotes. Asking for quotes with links keeps the summary honest, because you can check each claim against the thread.</p>
<h2>Keeping costs predictable</h2>
<p>Running an agent against a paid API is only comfortable if spend has a ceiling. The pieces that give you one:</p>
<ul>
<li><p><strong>A quote before every run.</strong> <code>inspect</code> returns <code>maxCharge</code>, and the charge never exceeds the quote. The Skill tells agents to state the price and wait for your OK when the quote is above $0.10.</p>
</li>
<li><p><strong>No overdraft.</strong> Per-result runs shrink to what your balance covers, per-call runs do not start without enough balance, and the account cannot go below zero.</p>
</li>
<li><p><strong>Spend limits per key or app.</strong> You can set daily or monthly limits in the dashboard. A run that hits one fails with <code>spend_limit</code> instead of charging.</p>
</li>
<li><p><strong>Caps on schedules.</strong> If you monitor threads on a schedule (<code>POST /v1/schedules</code>), each schedule takes a <code>runCap</code> and an optional <code>monthlyCap</code>, and <code>dryRun: true</code> previews the cost without saving anything.</p>
</li>
<li><p><strong>Failed runs.</strong> These are generally not charged; a partial charge, if any, stays within the quote.</p>
</li>
</ul>
<p>Billing is pay per use: some sources charge per call, others per result plus a per-run fee with a minimum. Most Reddit sources are per call. Current rates are on the <a href="https://prolevelmax.com/pricing">pricing page</a>, and new accounts get $1 of credit after email verification, which is enough to try the flow above.</p>
<h2>Limits worth knowing</h2>
<ul>
<li><p><strong>Public data only.</strong> Private and quarantined subreddits cannot be read, and anything that needs someone's login is not collected.</p>
</li>
<li><p><strong>Deleted content stays deleted.</strong> Removed or deleted posts and comments show only what Reddit still shows publicly, usually nothing.</p>
</li>
<li><p><strong>Scores are a snapshot.</strong> Upvotes and comment counts are the public numbers at the moment of the request.</p>
</li>
<li><p><strong>Concurrency.</strong> Up to 5 jobs run at once per account, across all keys and connections. Extra runs get <code>429 too_many_runs</code>.</p>
</li>
<li><p><strong>Run size and time.</strong> Per-result sources return up to 100 results unless you set <code>maxItems</code>. A synchronous run waits at most 300 seconds (<code>timeout</code>), so use <code>async</code> for big jobs.</p>
</li>
<li><p><strong>Retention.</strong> Run results are kept for 30 days. Download what you want to keep.</p>
</li>
</ul>
<h2>Where to go next</h2>
<ul>
<li><p>Docs: <a href="https://prolevelmax.com/docs">prolevelmax.com/docs</a></p>
</li>
<li><p>OpenAPI: <a href="https://prolevelmax.com/openapi.json">prolevelmax.com/openapi.json</a></p>
</li>
<li><p>Agent Skill: <a href="https://prolevelmax.com/SKILL.md">prolevelmax.com/SKILL.md</a></p>
</li>
<li><p>Client configs: <a href="https://github.com/tophmeee/prolevelmax-mcp">github.com/tophmeee/prolevelmax-mcp</a></p>
</li>
</ul>
<p>If something in the flow is unclear or broken, email <a href="mailto:toph@prolevelmax.com">toph@prolevelmax.com</a>.</p>
]]></content:encoded></item></channel></rss>