Ask a question and Consensus reads what people actually say about it on Reddit and X, groups the answers into a poll, and shows every opinion behind each option, with links back to the original comment or post.
Live: https://useconsensus.io
One search is one streamed request to /api/v3/search (netlify/functions/search-v3.mts). src/v3/run.ts wires the stages together and sends server-sent events as results arrive, so the poll fills in while the search is still running. Every stage has a time budget so the stream always finishes before the 60s function limit.
- Reddit pool (
reddit.ts): an LLM writes about 10 Google searches (thread-title rewordings plus wider ones). They run in parallel through Serper with the raw question. The top 40 threads are downloaded with their full comment trees, comments outside the date window are dropped, and bots are removed. - X pool (
x.ts,xpace.ts): an LLM writes narrow first-person opinion queries plus broad subject queries for TwitterAPI.io advanced search. These fan out into Top/Latest and popularity lanes that page in parallel. Every instance shares one global rate limiter with backoff, and per-search spend and time caps apply. - Options (
sort.ts): a sample of the pool becomes the poll options, plus an Other bucket. - Sorting (
sort.ts): every post is sorted into an option in parallel batches. The vote comes from three fast models; DeepSeek is primary. Each post gets a strength and arelevancescore built from directness, agreement and recency. Posts that don't answer the question are dropped, not counted. - Themes (
themes.ts): the detailed reasons people give ("A is better because ..."). They are multi-label and can span options. - Counts (
counts.ts): the final poll, theme counts and listed posts are reconciled so the numbers always match what is shown.
Events: meta, plan, progress, poll, posts, themes, reasons, authors, communities, done, error.
Vite + React + TypeScript. Landing is a search bar, and results show the poll, sources, themes, and every opinion with its author and link. Sign-in uses Firebase (Google); results are cached and shareable by id.
Node 22+.
npm install
cp .env.example .env # fill in your own keys; .env is gitignored
npm run v3 -- "is a standing desk worth it" # full pipeline in the terminal
npm run dev # netlify dev: frontend + functions on :8888
npm run buildYou need keys for Straitly (LLMs), TwitterAPI.io (X), Serper (Google search) and Oxylabs (residential proxy for Reddit).
LLM gateway: the pipeline runs on Straitly by default (STRAITLY_API_KEY), an OpenAI-compatible gateway at api.straitly.ai. OpenRouter is supported as an alternative (OPENROUTER_API_KEY; if both keys are set, Straitly is used unless LLM_GATEWAY=openrouter), and any other OpenAI-compatible gateway via LLM_GATEWAY=custom with LLM_BASE_URL, LLM_API_KEY, LLM_MODEL and LLM_USD_PER_1K_TOKENS. Model ids are the gateway's own, so each preset carries its own default model, sort voters and concurrency; override with LLM_MODEL, SORT_VOTERS and SORT_CONCURRENCY (see .env.example). LLM_PROVIDERS applies to OpenRouter only. Per-call cost is read from the response's usage.cost, which both Straitly and OpenRouter send. If a response has none, the call is metered at LLM_USD_PER_1K_TOKENS (required for custom gateways, 0.002 by default for the presets), so the daily spend cap never counts LLM calls as free.
Step-by-step setup, keys and troubleshooting: mcp/SETUP.md.
mcp/server.ts is a Model Context Protocol server over stdio. Any MCP client can then ask "what do people on Reddit and X think about X" and get the poll with linked quotes.
Tools:
consensus_get(id)replays a saved search from useconsensus.io (theid=in a result URL). Free, no keys.consensus_featured()lists the polls on the home page with their leading answer and id. Free, no keys.consensus_search(question, since?, until?, sources?)runs the full pipeline fromsrc/v3on your machine with the keys in.env, streams progress, and returns the poll plus what it cost. Each search bills your own LLM, Serper, Oxylabs and X keys (tens of cents); passsources: ["reddit"]without an X key.
Run it:
npm run mcp # same as: npx tsx --env-file-if-exists=.env mcp/server.tsAdd it to a client (use the absolute path of this checkout):
# Claude Code
claude mcp add consensus -- npx -y tsx --env-file-if-exists=/ABS/PATH/consensus/.env /ABS/PATH/consensus/mcp/server.ts// Cursor: ~/.cursor/mcp.json (or .cursor/mcp.json in a project)
// VS Code: .vscode/mcp.json Claude Desktop: claude_desktop_config.json
{ "mcpServers": { "consensus": { "command": "npx", "args": ["-y", "tsx", "--env-file-if-exists=/ABS/PATH/consensus/.env", "/ABS/PATH/consensus/mcp/server.ts"] } } }A search can take up to a minute; if your client times out tool calls sooner, raise its MCP tool timeout (Claude Code: MCP_TOOL_TIMEOUT). Try it with the inspector: npx @modelcontextprotocol/inspector npx tsx mcp/server.ts, then call consensus_get with ByAYXunnmt6BGCQR.
Each visitor can run one search at a time. Per-IP hourly rate limits, a daily spend cap (DAILY_SPEND_CAP_USD) and a global X request budget (X_GLOBAL_RPS) keep load and cost bounded. These are tunable through the environment variables in .env.example and the constants at the top of each src/v3 file.
The live site is Netlify useconsensus (8765d8ab-45da-46d0-b83b-bebc4e4af014) at https://useconsensus.io.
The source of truth is Straitly-ai/consensus, branch main. The historical Netlify repository link and
mutchtaba/consensus deployment workflow are not the production v3 release path.
Never deploy to consensus-api-mutchtaba to update useconsensus.io.
After review, tests, and explicit production approval, push the intended commit to main. The GitHub Actions
workflow deploys that exact checkout to the explicit site ID. Do not manually deploy production.
First validate a draft deployment with the same explicit site ID. Verify the published deploy ID and
a fresh, complete search with classified Reddit and X opinions before reporting success.
Keep the previous published deploy ID for rollback with Netlify's restore-deploy operation.
Production uses Straitly with LLM_MODEL=gpt-6-luna, SORT_VOTERS= (single-model classification),
SORT_CONCURRENCY=40, SORT_BATCH=25, POOL_CAP_REDDIT=1800, POOL_CAP_X=1800,
SORT_DEADLINE_MS=40000, and FINISH_BY_MS=52000. These are bounded settings, not an unlimited-load guarantee.
Run regression tests with node --import tsx --test scripts/llm.test.ts.
No keys live in this repo. Production functions read them from the host's environment variables. Report vulnerabilities privately to the maintainers rather than in a public issue.
MIT