recipe

You have the Reddit posts. Now find out who wants to buy.

Published · API details checked against the live spec the same day

Send the posts to SignalPipe’s batch endpoint, 25 at a time, each with an id of your own. Every post comes back with a verdict: whether it reads as a buyer, whether the three judges were needed, and whether they agreed. Clear-cut posts cost nothing. Your agent can do the same over MCP, one post at a time. This page covers only that scoring step. Collecting the posts is yours, and Reddit has rules about it.

Before you start: Reddit’s rules for its data

Reddit’s Responsible Builder Policy applies to everyone who uses Reddit data, including bots and AI agents. In short:

  • You must request access, and be approved, before accessing any Reddit data through its API.
  • Commercial use needs Reddit’s explicit written approval, and the policy says that extends to mining and scraping.
  • Apps must get consent before sending private messages, and must not spam posts, comments or messages.

So check that your source and your use are covered before you score anything, whether the posts come from an approved API app, a data provider or an MCP server your agent already uses. The same recipe works on any text you are allowed to use: Hacker News has an official API that needs no key, and the text in your own inbox, support queue and community is yours to score. For X, see scoring X posts.

What you need

1. Shape each post into an item

Put the title and body together as text (up to 4,000 characters are used), give each post an id from your own records, such as the Reddit fullname, and set source_hint to reddit. The id comes back untouched with its verdict; it is neither stored nor interpreted. include_drafting_context: false makes it a pure triage pass. Scoring comments? Put the post each one replies to in context: a comment like “same problem here, what did you end up using?” means little alone, and the judges read it in that light while still scoring the comment’s own author.

batch.json
{
  "product_id": "YOUR_PRODUCT_ID",
  "include_drafting_context": false,
  "items": [
    {
      "id": "t3_1abc2de",
      "text": "Our CRM contract ends in June and we're not renewing.\n\nWhat did your team move to?",
      "source_hint": "reddit"
    }
  ]
}

2. Send pages of 25

POST /signal/score_batch takes 1–25 items against one product. Items are scored one after another, and a malformed item fails on its own without taking the batch down.

python
import os
import requests

API = "https://api.signalpipe.io"
HEADERS = {"Authorization": f"Bearer {os.environ['SIGNALPIPE_OPERATOR_KEY']}"}
PRODUCT_ID = os.environ["SIGNALPIPE_PRODUCT_ID"]


def score(posts):
    """posts: dicts with 'id', 'title' and 'body', from a source you may use."""
    items = [
        {"id": p["id"], "text": f"{p['title']}\n\n{p.get('body') or ''}".strip(), "source_hint": "reddit"}
        for p in posts
    ]
    results = []
    for i in range(0, len(items), 25):  # the endpoint takes 1-25 items
        r = requests.post(
            f"{API}/signal/score_batch",
            headers=HEADERS,
            json={"product_id": PRODUCT_ID, "items": items[i:i + 25], "include_drafting_context": False},
            timeout=600,  # items are scored one after another; borderline ones convene the panel
        )
        r.raise_for_status()
        results.extend(r.json()["results"])
    return results


results = score(posts)
buyers = [r for r in results if r["ok"] and r["classification"] == "buying_intent"]
look_again = [r for r in results if r["ok"] and (r.get("degraded") or (r.get("swarm") or {}).get("split"))]
failed = [r for r in results if not r["ok"]]
javascript
const API = 'https://api.signalpipe.io'
const headers = {
  Authorization: `Bearer ${process.env.SIGNALPIPE_OPERATOR_KEY}`,
  'Content-Type': 'application/json',
}

// posts: [{ id, title, body }] from a source you may use
export async function score(posts, productId) {
  const items = posts.map(p => ({
    id: p.id,
    text: `${p.title}\n\n${p.body ?? ''}`.trim(),
    source_hint: 'reddit',
  }))
  const results = []
  for (let i = 0; i < items.length; i += 25) { // the endpoint takes 1-25 items
    const res = await fetch(`${API}/signal/score_batch`, {
      method: 'POST',
      headers,
      body: JSON.stringify({ product_id: productId, items: items.slice(i, i + 25), include_drafting_context: false }),
    })
    if (!res.ok) throw new Error(`score_batch ${res.status}: ${await res.text()}`)
    results.push(...(await res.json()).results)
  }
  return results
}
curl
# your product ids
curl -s https://api.signalpipe.io/products/list \
  -H "Authorization: Bearer $SIGNALPIPE_OPERATOR_KEY"

# score up to 25 posts
curl -s https://api.signalpipe.io/signal/score_batch \
  -H "Authorization: Bearer $SIGNALPIPE_OPERATOR_KEY" \
  -H "Content-Type: application/json" \
  -d @batch.json

3. Read the verdicts

response (trimmed)
{
  "count": 3,
  "scored": 2,
  "failed": 1,
  "judgements_spent": 1,
  "degraded_count": 0,
  "results": [
    {
      "id": "t3_1abc2de", "index": 0, "ok": true,
      "classification": "buying_intent",
      "swarm_ran": true, "panel_verdict": "kept",
      "swarm": { "judges": { "skeptic": "convinced", "analyst": "convinced", "optimist": "convinced" }, "split": false },
      "degraded": false
    },
    {
      "id": "t3_1fgh3ij", "index": 1, "ok": true,
      "classification": "noise",
      "swarm_ran": false, "swarm_skipped": "not_borderline", "panel_verdict": "not_run",
      "degraded": false
    },
    { "id": "t3_1klm4no", "index": 2, "ok": false, "error": "Missing required field: text" }
  ]
}

ok, error

Each item succeeds or fails on its own; a bad row never takes the batch down. Results come back in the order you sent them, with your id.

classification

The verdict: buying_intent, competitor_mention, borderline or noise.

swarm_ran, swarm_skipped

Whether the three judges were convened. not_borderline means the post was clear-cut and cost nothing.

panel_verdict

What the judges concluded: kept, rejected or not_run. They can raise a score but never lower it, so treat rejected as not a lead whatever the score.

swarm.judges, swarm.split

Present when the panel ran: each judge’s read, and split: true when they disagreed. Read those posts yourself.

degraded

True when the panel could not run (quota_exhausted, daily_cap_reached or panel_failed) and the post was decided on content alone. Re-check these.

score

A 0–100 number for ordering results. The classification is the verdict.

judgements_spent, swarm_budget_remaining

In the batch summary: panels actually convened, and what is left of your month.

A useful split: act on the buyers the judges agreed on, read the ones they split on, and re-check anything degraded. For what a buying signal looks like, and the lookalikes that fool keyword alerts, see buying signals.

The same thing over MCP

If your agent already reads posts through an MCP server, connect SignalPipe’s beside it and let the agent do the scoring. In Claude Code:

claude code
claude mcp add --transport http signalpipe https://api.signalpipe.io/mcp \
  --header "Authorization: Bearer YOUR_OPERATOR_KEY"

Or, for clients that take a config file, such as a project’s .mcp.json in Claude Code:

.mcp.json
{
  "mcpServers": {
    "signalpipe": {
      "type": "http",
      "url": "https://api.signalpipe.io/mcp",
      "headers": { "Authorization": "Bearer YOUR_OPERATOR_KEY" }
    }
  }
}

Then ask for the work in plain language:

prompt
Take the posts you just fetched. For each one, call SignalPipe's
score_signal tool with the title and body as text, product_id YOUR_PRODUCT_ID
and source_hint "reddit". For a comment, pass the post it replies to as context.
Then list the posts classified buying_intent, and separately any where the
judges split or the verdict is degraded.

MCP scores one post per call through score_signal, so for hundreds of posts the batch API is faster. The full tool list is on the agents page.

What it costs

Scoring is metered in judgements. One judgement is one run of the three-judge panel, and judgements_spent in each batch summary counts only panels actually convened. Posts that are clearly noise, or clearly a buyer, are decided without the panel and cost nothing, so a batch normally spends well below one judgement per post. Plans start at $29 a month for 3,000; see pricing.

Try one post first.

Paste a real post into the live panel and see the verdict and the three judges before you write any code. Free, and no account needed.

Questions

Do I need to give SignalPipe access to my Reddit account?

No. Scoring needs no Reddit credentials: you send the text of the posts, and SignalPipe returns a verdict on each. How you collect the posts is up to you, and must be allowed by Reddit’s terms.

What are Reddit’s rules for using its data?

Reddit’s Responsible Builder Policy requires approval before accessing Reddit data through its API, and explicit written approval for commercial use, which it says extends to scraping. It also requires an app to get consent before sending private messages, and bans automated spam. Check that your source and your use are covered before you score anything.

How many posts can I score per call?

Up to 25 per call to POST /signal/score_batch. Send larger sets as pages of 25. Results come back in the order you sent them, each carrying the id you gave it.

What does it cost?

Scoring is metered in judgements: one judgement is one run of the three-judge panel. Posts that are clearly noise, or clearly a buyer, are decided without the panel and cost nothing, so a batch normally spends well below one judgement per post. Plans start at $29 a month for 3,000 judgements.

Can I do this from Claude Code or Cursor?

Yes. Connect SignalPipe’s MCP server at https://api.signalpipe.io/mcp with the header Authorization: Bearer and your operator key, then ask your agent to score posts with the score_signal tool. MCP scores one post per call; for hundreds of posts, the batch API is faster.

What should I send as the text?

The post’s title and body together. Replies can be scored on their own. Up to 4,000 characters of each text are used.

Is the id I send stored?

No. The id is echoed back so you can match each verdict to your own records; it is neither stored nor interpreted.

Sources