Skip to content

Repository files navigation

Reddit Prospect Miner

A small CLI for finding warm prospects on Reddit — people who have already declared intent about the problem your product solves, surfaced from the comments and threads they've written themselves.

Cold DMs are a tax on attention. This tool inverts that: it reads public Reddit activity, scores users against an ICP you define in YAML, and hands you a ranked shortlist with the exact comment that triggered the match — so your first message can refer to something they actually said.

Output flows two places at once:

  1. A Notion database (your working tracker — one row per prospect)
  2. A local markdown file at outputs/prospects_YYYYMMDD.md (audit log + a single human-readable artifact for each run)

ICP definition, signal weights, and the DM voice template are all configuration. There is nothing about the tool that is specific to a single domain — point it at any subreddit ecosystem and any set of intent keywords.


Table of contents


What it does

  1. Searches the subreddits you list for comments matching your intent keywords, within a configurable time window.
  2. Pulls each commenter's recent activity — last 25 comments + 10 posts, account age, and karma — cached on disk so re-runs are cheap.
  3. Scores them against a YAML-defined ruleset of strong/medium signal patterns, with per-rule caps. Disqualifying conditions (account too new, too much self-promotion, etc.) filter the list.
  4. Generates a personalized opener for each top candidate using Gemini, grounded in a DM template you write (outreach.md).
  5. Writes the top N to your Notion tracker and to a dated markdown file.

The tool does one thing and exits. No daemon, no web UI, no scheduler.

Who it's for

People doing customer development or outbound sales who want to build a list of warm prospects they can reach out to as peers — not strangers. Useful for:

  • Indie SaaS founders looking for early users
  • Consultants whose ICP gathers in specific subreddits
  • Researchers building qualitative interview lists
  • Anyone tired of cold-DMing people who never asked

Install

git clone <repo-url> reddit-prospect-miner
cd reddit-prospect-miner
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env
cp config.example.yaml config.yaml

Then edit .env with your credentials and config.yaml with your ICP. See the next two sections.

Setup walkthrough

1. Reddit API credentials

  1. Sign in to Reddit and go to https://www.reddit.com/prefs/apps/.
  2. Click create another app... at the bottom.
  3. Fill in:
    • name: anything (e.g. prospect-miner)
    • type: select script
    • redirect uri: http://localhost:8080 (required field, not actually used for script apps)
    • leave the other fields blank
  4. Click create app.
  5. You'll see your app listed. The client ID is the short string under the app name (about 14 characters). The client secret is the longer string labeled secret.
  6. Paste both into .env:
    REDDIT_CLIENT_ID=<short string>
    REDDIT_CLIENT_SECRET=<longer string>
    
  7. Set a descriptive user_agent in your config.yaml under the reddit: section — Reddit's API guidelines require it to identify you.

The miner uses Reddit in read-only mode, so no OAuth flow is needed.

2. Notion integration

  1. Go to https://www.notion.so/profile/integrations and click + New integration.
  2. Give it a name (e.g. Prospect Miner), associate it with your workspace, and submit.
  3. Copy the Internal Integration Token (starts with secret_ or ntn_) and paste into .env:
    NOTION_API_KEY=<your token>
    
  4. Open your prospects database in Notion. Click the ··· menu in the top-right → Connections → search for your integration name → Confirm. Without this step the API will return a 404, even with a valid token.
  5. Find your database's ID. Open the database as a full page; the URL looks like:
    https://www.notion.so/<workspace>/<database-id>?v=<view-id>
    
    The 32-character hex string before the ? is the ID (with dashes added automatically: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).
  6. Paste that ID into config.yaml under output.notion_data_source_id.

Database schema: the miner expects ten properties on your database, listed under notion.fields in config.example.yaml. The property types can be whatever fits your workflow (e.g. Status can be a select, status, or text property — the writer detects the type and adapts). If your existing database uses different property names, update notion.fields in your config to map to your names instead.

The ten expected fields:

Default name Recommended type What gets written
Name title u/<reddit_username>
Platform select / text Reddit
Link url profile URL
Product Focus select / text from notion.defaults.product_focus
Source Type select / text Intent capture - Reddit
Original Signal rich text trigger permalink + first 200 chars of comment
Status status / select Identified (move to Contacted after DM)
Next Action select / text Send Reddit DM
Next Action Date date today + 1 weekday
Notes rich text structured markdown: score, why-fit, opener, context

The miner skips creating a row if a row with the same Name already exists.

3. Gemini API key (optional, for openers)

By default the miner uses Gemini to write a personalized opener for each prospect, grounded in the DM template you keep in outreach.md.

  1. Sign in at https://aistudio.google.com/apikey and create an API key.
  2. Paste into .env:
    GEMINI_API_KEY=<your key>
    
  3. Edit outreach.md to reflect your real DM voice and structure. The LLM reads this file as a reference for how you want openers to sound.

If you'd rather skip the LLM step, set opener.enabled: false in your config (or pass --skip-opener). The Notes field will then contain a fallback showing the trigger comment, and you write the opener yourself.

Configure your ICP

config.example.yaml ships with a generic "indie SaaS founders interested in cold email" ICP — it's there so you can see the shape and run a dry test. Copy it to config.yaml and edit:

  • target.subreddits — where your ICP hangs out
  • target.intent_keywords — phrases people use when they have the problem
  • target.time_window_days — how far back to look (Reddit's search is coarse; 90 is a good default)
  • scoring.strong_signals / medium_signals — regex patterns that characterize a good-fit user. Each has a weight; medium signals can have a cap to prevent one pattern from dominating.
  • scoring.disqualifying — conditions like account_age_days < 60 or self_promo_ratio > 0.4 that exclude a candidate entirely. Supports field op number expressions against this namespace: account_age_days, total_karma, comment_count, post_count, self_promo_ratio.
  • output.top_n — how many candidates to write per run (30 default)

Run

source .venv/bin/activate
python -m reddit_prospect_miner --config config.yaml

Useful flags:

# Dry run — no Notion writes, no LLM calls
python -m reddit_prospect_miner --skip-notion --skip-opener

# Smaller top-N for testing
python -m reddit_prospect_miner --top-n 5

# Verbose (PRAW + httpx debug)
python -m reddit_prospect_miner -v

The first run takes longer because nothing is cached. Subsequent runs against the same usernames within the same day re-use cached histories from cache/<username>_YYYYMMDD.json.

Output

Each run produces:

  • Notion rows — one per qualifying top-N prospect, idempotent (dedups by Name).
  • outputs/prospects_YYYYMMDD.md — the same content as a single scannable markdown file. Per candidate: score, why-fit, signals breakdown, trigger comment in a blockquote, suggested opener, profile/karma context.

Stdout prints progress as the run proceeds, plus a summary at the end (triggers seen / unique users / scored / qualifying / Notion results).

How it works

intent search (subreddit × keyword)
        │
        ▼
trigger comments  ─►  dedupe by author
        │
        ▼
fetch history (cached) ─► Candidate(trigger, history)
        │
        ▼
score against rules ─► ScoredCandidate(score, hits, disqualifications)
        │
        ▼
rank → top N
        │
        ▼
Gemini opener (template-grounded) ─► Prospect(scored, opener)
        │
        ├──► Notion writer (schema-aware, deduped)
        └──► Markdown writer (outputs/prospects_YYYYMMDD.md)

Each module is independent and small (~150 LOC). The scoring engine has no Reddit knowledge; the Notion writer has no scoring knowledge. Productization is a matter of swapping notion_writer for a CRM writer, or putting the same pipeline behind a job runner.

Project layout

reddit_prospect_miner/
  __init__.py
  __main__.py            # python -m reddit_prospect_miner
  cli.py                 # argparse + orchestration
  config.py              # YAML loader, typed dataclasses
  models.py              # Candidate, ScoredCandidate, Prospect, ...
  reddit_client.py       # PRAW wrapper, search, history fetch, on-disk cache
  scoring.py             # signal matching + disqualification engine
  opener.py              # Gemini opener generation, with safe fallback
  notion_writer.py       # schema-aware Notion writer + dedupe
  markdown_writer.py     # local audit log
tests/
  test_scoring.py
  test_config.py
config.example.yaml      # generic ICP example
outreach.md              # your DM template (LLM voice reference)
.env.example
requirements.txt
LICENSE                  # MIT

Limits

  • Reddit search is coarse. PRAW's subreddit.search() returns submissions matching the query, then the miner walks those threads' comments. Comments in threads whose submission doesn't match the keyword will be missed. For most ICPs this is fine — people who declare intent tend to do so in threads about the topic.
  • Rate-limited. Default rate_limit_seconds: 1.1 between Reddit calls. A full run over 5 subreddits × 13 keywords takes 5–15 minutes for the search phase, plus ~1s/user for history.
  • Gemini cost. One LLM call per top-N candidate. With top_n: 30 and a 500-token prompt, the cost is negligible ($0.01/run on gemini-2.5-flash) but not zero.
  • Notion writes are not transactional. If the script dies mid-write, re-running is safe — dedupe by Name prevents duplicates.

License

MIT — fork it, modify it, run it commercially, build a product on top of it. Attribution appreciated but not required.

About

Find warm prospects on Reddit by mining declared-intent signals. Configurable ICP scoring, Notion or markdown output.

Topics

Resources

Contributing

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages