# Reddit Opportunity Finder (`signalflow_studio/reddit-opportunity-finder`) Actor

Find Reddit-backed opportunities from up to 30 posts and 150 comments by default, with evidence URLs, transparent research plans, and optional BYOK AI.

- **URL**: https://apify.com/signalflow\_studio/reddit-opportunity-finder.md
- **Developed by:** [SignalFlow Studio](https://apify.com/signalflow_studio) (community)
- **Categories:** Social media, AI, Lead generation
- **Stats:** 2 total users, 1 monthly users, 20.0% runs succeeded, NaN bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.30 / report generated

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## Reddit Opportunity Finder

Reddit Opportunity Finder turns public Reddit discussions into product opportunity, competitor complaint, and buying-intent reports. It is not a generic Reddit scraper: it is an evidence-backed research Actor for founders, product teams, agencies, growth marketers, and AI agents.

The Actor fetches bounded public Reddit posts and comments, extracts pain signals, scores buying intent, identifies mentioned tools and competitors, and returns actionable opportunities with Reddit evidence URLs.

### Run Size and Cost Controls

The default public run is intentionally bounded so users can understand the amount of Reddit data being processed before they click Run:

- Fetches up to 30 public Reddit posts.
- Analyzes up to 8 public comments per post.
- Analyzes up to 150 comments total.
- Produces up to 10 ranked opportunities.
- Includes Markdown, JSON, CSV, citations, evidence URLs, alerts, and monitoring fields in the same report.

The report is a structured research output, not a raw Reddit scrape. Users can lower Apify platform usage by reducing posts/comments or turning comments off, and can increase coverage within the hard caps when they need deeper research.

Optional AI enhancement is separate from the baseline rules-based report. BYOK model usage is billed by the selected AI provider to the API key owner.

### What You Get

- Direct answer for your analysis goal.
- Scored product opportunities with suggested product angles.
- Buying-intent score for each opportunity and evidence signal.
- Competitor and tool mentions from Reddit discussions.
- Evidence URLs back to public Reddit posts/comments.
- Recommended validation actions.
- Alerts for high-scoring opportunities and competitor mention spikes.
- Monitoring comparison against a previous report.
- Markdown, JSON, and CSV outputs.
- Optional BYOK AI clustering and report polish.

### Typical Use Cases

- Find Shopify app ideas from Reddit pain points.
- Monitor competitor complaints in an AI SaaS category.
- Validate whether a niche has enough buying-intent signals.
- Find content angles from repeated Reddit questions.
- Run a scheduled Reddit market monitor and alert only on new/high-score signals.
- Use the Actor from an AI agent through Apify MCP with predictable structured outputs.

### Quick Start

```bash
npm install
npm run smoke
npm run validate:smoke
````

Real-data smoke input:

```json
{
  "mode": "reddit_discovery",
  "analysisGoal": "Find Reddit-backed micro SaaS opportunities from pricing pain, manual workflows, and alternative requests.",
  "market": "micro SaaS and ecommerce operations",
  "subreddits": ["SaaS", "indiehackers", "shopify"],
  "keywords": ["expensive", "alternative", "looking for", "manual"],
  "timeRange": "month",
  "maxPosts": 30,
  "maxCommentsPerPost": 8,
  "maxTotalComments": 150,
  "maxOpportunities": 10,
  "outputFormat": "markdown"
}
```

Local outputs:

- `outputs/smoke/output.json`
- `outputs/smoke/report.md`
- `outputs/smoke/report.csv`

### Presets

Use `preset` to start faster. You can still override subreddits, keywords, competitors, and scoring options.

- `custom`
- `shopify_app_opportunities`
- `ai_saas_competitor_complaints`
- `creator_tools`
- `local_business_software`
- `b2b_sales_tools`

Example:

```json
{
  "mode": "reddit_discovery",
  "preset": "shopify_app_opportunities",
  "analysisGoal": "What Shopify app opportunities show strong Reddit pain and buying intent?",
  "maxPosts": 25,
  "maxOpportunities": 8
}
```

### Main Inputs

The Apify input form is grouped into:

- Start here: analysis goal, preset, market, Reddit sources, and main limits.

- Reddit source: known competitors, time range, and sorting.

- AI / BYOK: model provider, user API key, and AI limits.

- Monitoring: previous report, alert threshold, and scheduled-run controls.

- Advanced controls: quality filters, comments, output options, proxy, and debug mode.

- `analysisGoal`: agent-friendly research goal. It guides keyword hints and report framing; explicit subreddits and keywords remain the source-of-truth for Reddit collection.

- `market`: market or niche context.

- `decisionGoal`: `find_product_opportunities`, `monitor_competitors`, `validate_market`, `find_content_angles`, or `detect_alerts`.

- `subreddits`: public subreddits without `r/`.

- `keywords`: search and analysis terms.

- `knownCompetitors`: tools or competitors to track.

- `timeRange`: `day`, `week`, `month`, `year`, or `all`.

- `sort`: `relevance`, `new`, `top`, or `comments`.

- `maxPosts`, `maxCommentsPerPost`, `maxTotalComments`: bounded fetch controls.

- `minScore`, `minBuyingIntent`, `dedupeStrength`: quality controls.

- `trendMode`, `previousReport`, `monitoringOnly`, `alertThreshold`: monitoring controls.

### BYOK AI

AI is optional. The rules-based analyzer always runs first and does not require a key.

Supported providers:

- `deepseek`
- `openai`
- `openrouter`
- `anthropic`
- `gemini`
- `custom_openai_compatible`

Inputs:

- `aiProvider`: provider name.
- `aiMode`: `off`, `polish`, `cluster`, or `full`.
- `aiApiKey`: user-provided secret key. When this is set, model API costs are billed by the selected AI provider to the key owner.
- Public BYOK runs use only the user-provided `aiApiKey`. Users must provide their own key for AI features.
- `aiModel`: model name for the selected provider.
- `aiBaseUrl`: optional override. Leave empty for provider default.
- `maxAiInputChars`, `maxAiOutputTokens`: call-size limits. They reduce risk but are not exact billing caps.

AI safety behavior:

- The Actor sends compact extracted signals, not unbounded raw Reddit dumps.
- AI must return `signalId` references for evidence.
- The program validates evidence IDs and backfills Reddit URLs from extracted signals.
- If AI fails or no key is available, the rules-based report still returns and records the reason in `summary.ai`.

### Cost Model

There are three separate cost layers:

- Actor pay-per-event fees: paid by the run owner only after the Actor owner configures PPE events in Apify Console.
- Apify platform usage: for the normal PPE mode, this is included in the event price and is deducted from the Actor owner's revenue/profit calculation. If the Actor owner enables "Pay per event + usage", the run owner pays platform usage separately.
- AI provider usage: paid by the owner of the API key used for `aiApiKey`. Public users cannot trigger owner-paid AI fallback from the visible input schema.

Current public setup: platform usage is not passed through separately, so users see simple PPE event pricing. Higher post/comment limits can increase platform usage and reduce the Actor owner's margin, but hard caps and the internal fetch budget keep runs bounded. If monthly PPE revenue does not cover platform costs for this Actor, Apify reports that Actor's profit as zero rather than subtracting the loss from other Actors.

Pricing risk controls:

- Current primary event: `report_generated` at `$0.30`; owner gross revenue after Apify margin is `$0.24` before platform costs.
- Observed cloud platform cost after the fetch-budget fix was `$0.0253` for a real Reddit run with 8 discussions and 5 opportunities. A previous manually started long run cost about `$0.078`, which is still below the current primary-event owner gross revenue but showed why fixed pricing needs hard caps.
- Public default run size is bounded at 30 posts and 150 comments; hard caps are 50 posts and 500 comments.
- Internal Reddit fetch budget is 120 seconds so source fetching should not consume the full Apify run timeout.
- Public users cannot trigger owner-paid AI fallback. BYOK model costs are paid by the user's own model provider account.

Recommended public setup:

- Standard report PPE: one successful `report_generated` event per completed report.
- Optional AI enhancement PPE: one `ai_cluster_generated` add-on event when BYOK AI enhancement succeeds.
- Default data volume included in the report: up to 30 public Reddit posts, up to 8 comments per post, and up to 150 comments total.
- Hard caps: 50 posts, 20 comments per post, and 500 comments total.
- Keep `aiProvider: "none"` and `aiMode: "off"` by default.
- Recommend BYOK for AI features.
- Configure PPE events before relying on Actor revenue.

### Agent-Ready Output

The first Dataset item includes:

- `answer`: direct answer to `analysisGoal`.
- `confidence`: `low`, `medium`, or `high`.
- `topOpportunities[]`: compact ranked opportunities for agents.
- `recommendedActions[]`: next actions tied to evidence.
- `citations[]`: evidence URLs and quotes.
- `alerts[]`: high-score opportunities or competitor mention spikes.
- `monetization`: PPE charge attempts and charged counts.
- `opportunities[]`: full opportunity objects.
- `summary`: counts, AI status, top themes, top entities.
- `themeMap`: signal counts by theme.
- `evidenceTable`: compact evidence rows.
- `monitoringSummary`: new/recurring/rising comparison when enabled.
- `markdownReport`: full human-readable report.
- `csvReport`: opportunity export.

Each opportunity includes `title`, `score`, `buyingIntentScore`, `targetUser`, `painSummary`, `suggestedProduct`, `entities`, `evidence[]`, `risks`, and `nextValidationStep`.

The Actor also defines `.actor/output_schema.json`, so Apify Console and AI agents can discover where to read Dataset, JSON, Markdown, and CSV outputs.

### Monitoring Mode

For scheduled runs:

1. Run the Actor once and save `outputs/smoke/output.json` or the Dataset item.
2. Pass that JSON into `previousReport`.
3. Enable `trendMode`.
4. Optionally enable `monitoringOnly` and set `alertThreshold`.

The output will include new opportunities, recurring opportunities, rising themes, new competitor mentions, and alerts.

### Local Commands

```bash
npm run sample
npm run validate:sample
npm run sample:ai
npm run validate:sample-ai
npm run preset
npm run validate:preset
npm run smoke
npm run validate:smoke
npm test
```

Run a custom input:

```bash
node src/main.js --input path/to/input.json --output-dir outputs/custom
```

### Apify Deployment

Actor files:

- `.actor/actor.json`
- `.actor/input_schema.json`
- `.actor/output_schema.json`
- `Dockerfile`
- `src/main.js`

Deploy:

```bash
npx apify-cli@1.6.1 push
```

Cloud Actor:

- Actor ID: `BXI7bc4uZD7wvgoA4`
- Console page: `https://console.apify.com/actors/BXI7bc4uZD7wvgoA4`
- Store URL after publishing: `https://apify.com/signalflow_studio/reddit-opportunity-finder`
- Default cloud run options: latest build, 1024 MB memory, 600 second timeout.
- Internal Reddit fetch budget: 120 seconds, then the Actor continues with partial real data so slow Reddit/proxy requests do not consume the full run timeout.

Recent verification runs:

- Cloud latest pricing/runtime smoke: `K3fHgnaK5pjf8fgAZ`, build `0.1.26`, status `SUCCEEDED`, 20 real Reddit discussions, 5 opportunities, `REPORT.md` generated, `report_generated` charged once in `OUTPUT.json`, platform usage cost `$0.0353`, default timeout 600 seconds.
- Cloud pricing/runtime run: `4ldfGBBJggc7YhRv9`, build `0.1.22`, status `SUCCEEDED`, 8 real Reddit discussions, 5 opportunities, `REPORT.md` generated, `report_generated` charged once in `OUTPUT.json`, platform usage cost `$0.0253`.
- Cloud timeout regression run: `Ziy1xxJP91u9iOqHR`, build `0.1.21`, status `TIMED-OUT`; fixed by reducing the internal Reddit fetch budget to 120 seconds in build `0.1.22`.
- Cloud sample AI run: `28wtwpTcDoJzyRT1q`, status `SUCCEEDED`, 9 opportunities, output schema links present, DeepSeek cluster and polish enabled.
- Cloud real-data smoke run: `peTXxbSqLk78gM7Ds`, status `SUCCEEDED`, 8 posts, 7 comments, 9 signals, 5 opportunities, answer/citations/alerts present.
- Cloud latest-build sample run: `In6i9rqckFyHj6PJ6`, build `0.1.15`, status `SUCCEEDED`, 6 opportunities, output schema links present.
- Cloud missing-user-key AI run: `cSiyZD7I06SipymTa`, build `0.1.15`, status `SUCCEEDED`, AI disabled because no user `aiApiKey` was provided.

Cloud Reddit note: direct Reddit JSON requests from Apify datacenter IPs can return HTTP 403. `proxyType: "auto"` uses direct requests locally and Apify RESIDENTIAL proxy in cloud. Keep runs bounded and expect occasional source-level fetch errors.

### Compliance

- Only bounded public Reddit posts/comments are fetched.
- The Actor does not log in, post, comment, vote, profile users, bypass access controls, train models on Reddit data, or resell raw Reddit data.
- Users are responsible for ensuring their use complies with Reddit terms, Apify terms, and applicable laws.
- Use it for product research, opportunity discovery, and market intelligence, not harassment, spam, surveillance, or platform manipulation.

### Monetization Notes

The Actor is designed for Pay-per-event monetization. The code attempts these Apify PPE charges and records the result in `summary.monetization`:

- `report_generated`
- `ai_cluster_generated`

Recommended first-stage event setup:

- `report_generated`: primary event, one time per successful report.
- `ai_cluster_generated`: optional event when BYOK AI enhancement succeeds.

Alerts are included in the report and are not charged as a separate default event.

Recommended public defaults:

- `aiProvider: "none"` and `aiMode: "off"` by default.
- Users who enable AI should set `aiApiKey`; those model costs are billed to their own AI provider account.
- Public AI usage is BYOK only. The Actor does not read owner environment AI keys for public runs.

If PPE is not configured in Apify Console or through the Actor API, runs still complete and the events are recorded with `chargedCount: 0`.

# Actor input Schema

## `analysisGoal` (type: `string`):

What should this Reddit intelligence report answer? This guides keyword hints and report framing; explicit subreddits and keywords still control Reddit collection.

## `preset` (type: `string`):

Optional vertical preset that fills default Reddit communities, keywords, competitors, and scoring focus. Custom keeps full manual control.

## `market` (type: `string`):

Market context used in search terms, report answer, and opportunity framing.

## `decisionGoal` (type: `string`):

What the report should help decide.

## `maxPosts` (type: `integer`):

Maximum public Reddit posts to fetch. Default public run analyzes up to 30 posts; hard-capped at 50 to keep runtime and proxy usage bounded.

## `maxOpportunities` (type: `integer`):

Maximum opportunities to output.

## `subreddits` (type: `array`):

Public subreddits to search, without the r/ prefix. Presets provide defaults; explicit values override preset sources.

## `keywords` (type: `array`):

Pain-point and product-discovery phrases to search for. analysisGoal and market add transparent keyword hints.

## `knownCompetitors` (type: `array`):

Optional tool/competitor names to track in entity recognition.

## `timeRange` (type: `string`):

Reddit search time range.

## `sort` (type: `string`):

Reddit search sort.

## `aiProvider` (type: `string`):

Optional BYOK AI provider. Rules-based Reddit intelligence always runs first and does not require an AI key.

## `aiMode` (type: `string`):

polish adds final insight, cluster groups evidence signals into stronger opportunities, full does both.

## `aiApiKey` (type: `string`):

User-provided BYOK key. When set, model API costs are billed by the selected AI provider to the key owner.

## `aiModel` (type: `string`):

Model name for the selected provider, for example deepseek-chat, gpt-4.1-mini, openai/gpt-4.1-mini, claude-3-5-haiku-latest, or gemini-2.0-flash.

## `aiBaseUrl` (type: `string`):

Optional API base URL. Leave empty to use the selected provider default.

## `maxAiInputChars` (type: `integer`):

Maximum characters sent to the AI provider. This limits call size but is not an exact billing cap.

## `maxAiOutputTokens` (type: `integer`):

Maximum AI output tokens. This limits call size but is not an exact billing cap.

## `trendMode` (type: `boolean`):

Add monitoring-style new/recurring/rising summaries.

## `previousReport` (type: `object`):

Optional previous output JSON for monitoring comparisons.

## `monitoringOnly` (type: `boolean`):

Only keep opportunities that pass alertThreshold after analysis. Useful for scheduled alerts.

## `alertThreshold` (type: `integer`):

Opportunity score threshold for alert generation and monitoringOnly filtering.

## `dedupeAgainstPrevious` (type: `boolean`):

Compare current opportunities against previousReport when provided.

## `includeComments` (type: `boolean`):

Fetch and analyze public comments as supporting evidence. Comments improve evidence depth but can increase runtime and Apify platform usage.

## `maxCommentsPerPost` (type: `integer`):

Maximum comments to fetch per post. Default is 8; hard-capped at 20.

## `maxTotalComments` (type: `integer`):

Maximum comments to analyze across all posts. Default public run analyzes up to 150 comments; hard-capped at 500.

## `maxInputChars` (type: `integer`):

Maximum characters of combined post/comment text to analyze.

## `analysisDepth` (type: `string`):

Controls intended analysis depth. Deep mode is best with AI clustering enabled.

## `opportunityFocus` (type: `string`):

Bias scoring toward a specific type of opportunity.

## `reportType` (type: `string`):

Opportunity lens for titles, suggested products, and scoring.

## `minScore` (type: `integer`):

Filter out opportunities below this score.

## `minBuyingIntent` (type: `integer`):

Filter out low-intent signals before opportunity clustering.

## `minDistinctEvidencePosts` (type: `integer`):

Minimum distinct Reddit threads required before an opportunity is emitted.

## `dedupeStrength` (type: `string`):

How aggressively similar evidence signals are deduplicated.

## `excludeKeywords` (type: `array`):

Skip posts/comments that contain these terms.

## `trackEntities` (type: `boolean`):

Extract mentioned tools/competitors from public posts and comments.

## `excludePromotionalPosts` (type: `boolean`):

Skip obvious launch/self-promotion posts as primary evidence while still analyzing their comments.

## `outputFormat` (type: `string`):

Primary report format. Markdown is always generated as well.

## `includeRawSignals` (type: `boolean`):

Include sanitized extracted signal objects in output JSON.

## `includeEvidenceTable` (type: `boolean`):

Include a compact evidence table in JSON and Markdown.

## `language` (type: `string`):

Language hint for summaries and AI output.

## `reportTone` (type: `string`):

Audience lens for report phrasing.

## `proxyType` (type: `string`):

Use auto for direct local requests and Apify RESIDENTIAL proxy in cloud. Use none only for local debugging.

## `mode` (type: `string`):

Use reddit\_discovery for real public Reddit analysis. analyze\_dataset is only for testing with Reddit-like JSON.

## `posts` (type: `array`):

Reddit-like posts for analyze\_dataset mode only.

## `businessQuestion` (type: `string`):

Backward-compatible alias. Prefer analysisGoal.

## `useAi` (type: `boolean`):

Backward-compatible alias accepted to prevent old inputs from failing. Prefer aiProvider and aiMode.

## `useOwnerAiKey` (type: `boolean`):

Backward-compatible alias accepted to prevent old inputs from failing. Ignored in public runs; AI only uses aiApiKey.

## `aiEnhancement` (type: `string`):

Backward-compatible alias. Prefer aiProvider and aiMode.

## `deepseekModel` (type: `string`):

Backward-compatible alias. Prefer aiModel.

## Actor input object example

```json
{
  "analysisGoal": "Find Reddit-backed product opportunities with repeated pain, buying intent, and evidence URLs.",
  "preset": "custom",
  "market": "micro SaaS and ecommerce operations",
  "decisionGoal": "find_product_opportunities",
  "maxPosts": 30,
  "maxOpportunities": 10,
  "subreddits": [
    "SaaS",
    "indiehackers",
    "shopify"
  ],
  "keywords": [
    "expensive",
    "alternative",
    "looking for",
    "manual"
  ],
  "timeRange": "month",
  "sort": "relevance",
  "aiProvider": "none",
  "aiMode": "off",
  "maxAiInputChars": 30000,
  "maxAiOutputTokens": 1800,
  "trendMode": false,
  "monitoringOnly": false,
  "alertThreshold": 70,
  "dedupeAgainstPrevious": false,
  "includeComments": true,
  "maxCommentsPerPost": 8,
  "maxTotalComments": 150,
  "maxInputChars": 200000,
  "analysisDepth": "standard",
  "opportunityFocus": "balanced",
  "reportType": "micro_saas_ideas",
  "minScore": 0,
  "minBuyingIntent": 0,
  "minDistinctEvidencePosts": 2,
  "dedupeStrength": "medium",
  "trackEntities": true,
  "excludePromotionalPosts": true,
  "outputFormat": "markdown",
  "includeRawSignals": false,
  "includeEvidenceTable": true,
  "language": "en",
  "reportTone": "operator",
  "proxyType": "auto",
  "mode": "reddit_discovery",
  "businessQuestion": "",
  "useAi": false,
  "useOwnerAiKey": false,
  "aiEnhancement": "off",
  "deepseekModel": ""
}
```

# Actor output Schema

## `datasetItems` (type: `string`):

Structured JSON output. The first item includes answer, confidence, topOpportunities, opportunities, citations, alerts, summary.researchPlan, monetization, monitoringSummary, markdownReport, and csvReport.

## `jsonReport` (type: `string`):

Full JSON report stored in the default key-value store.

## `markdownReport` (type: `string`):

Human-readable Markdown report stored in the default key-value store.

## `csvReport` (type: `string`):

CSV export of opportunity rows stored in the default key-value store.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "analysisGoal": "Find Reddit-backed product opportunities with repeated pain, buying intent, and evidence URLs.",
    "market": "micro SaaS and ecommerce operations",
    "subreddits": [
        "SaaS",
        "indiehackers",
        "shopify"
    ],
    "keywords": [
        "expensive",
        "alternative",
        "looking for",
        "manual"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("signalflow_studio/reddit-opportunity-finder").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "analysisGoal": "Find Reddit-backed product opportunities with repeated pain, buying intent, and evidence URLs.",
    "market": "micro SaaS and ecommerce operations",
    "subreddits": [
        "SaaS",
        "indiehackers",
        "shopify",
    ],
    "keywords": [
        "expensive",
        "alternative",
        "looking for",
        "manual",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("signalflow_studio/reddit-opportunity-finder").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "analysisGoal": "Find Reddit-backed product opportunities with repeated pain, buying intent, and evidence URLs.",
  "market": "micro SaaS and ecommerce operations",
  "subreddits": [
    "SaaS",
    "indiehackers",
    "shopify"
  ],
  "keywords": [
    "expensive",
    "alternative",
    "looking for",
    "manual"
  ]
}' |
apify call signalflow_studio/reddit-opportunity-finder --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=signalflow_studio/reddit-opportunity-finder",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Reddit Opportunity Finder",
        "description": "Find Reddit-backed opportunities from up to 30 posts and 150 comments by default, with evidence URLs, transparent research plans, and optional BYOK AI.",
        "version": "0.1",
        "x-build-id": "E5PdSzAQOvS4VXGIG"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/signalflow_studio~reddit-opportunity-finder/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-signalflow_studio-reddit-opportunity-finder",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/signalflow_studio~reddit-opportunity-finder/runs": {
            "post": {
                "operationId": "runs-sync-signalflow_studio-reddit-opportunity-finder",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/signalflow_studio~reddit-opportunity-finder/run-sync": {
            "post": {
                "operationId": "run-sync-signalflow_studio-reddit-opportunity-finder",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "mode"
                ],
                "properties": {
                    "analysisGoal": {
                        "title": "Analysis goal",
                        "type": "string",
                        "description": "What should this Reddit intelligence report answer? This guides keyword hints and report framing; explicit subreddits and keywords still control Reddit collection."
                    },
                    "preset": {
                        "title": "Reddit intelligence preset",
                        "enum": [
                            "custom",
                            "shopify_app_opportunities",
                            "ai_saas_competitor_complaints",
                            "creator_tools",
                            "local_business_software",
                            "b2b_sales_tools"
                        ],
                        "type": "string",
                        "description": "Optional vertical preset that fills default Reddit communities, keywords, competitors, and scoring focus. Custom keeps full manual control.",
                        "default": "custom"
                    },
                    "market": {
                        "title": "Market or niche",
                        "type": "string",
                        "description": "Market context used in search terms, report answer, and opportunity framing."
                    },
                    "decisionGoal": {
                        "title": "Decision goal",
                        "enum": [
                            "find_product_opportunities",
                            "monitor_competitors",
                            "validate_market",
                            "find_content_angles",
                            "detect_alerts"
                        ],
                        "type": "string",
                        "description": "What the report should help decide.",
                        "default": "find_product_opportunities"
                    },
                    "maxPosts": {
                        "title": "Max posts",
                        "minimum": 1,
                        "maximum": 50,
                        "type": "integer",
                        "description": "Maximum public Reddit posts to fetch. Default public run analyzes up to 30 posts; hard-capped at 50 to keep runtime and proxy usage bounded.",
                        "default": 30
                    },
                    "maxOpportunities": {
                        "title": "Max opportunities",
                        "minimum": 1,
                        "maximum": 25,
                        "type": "integer",
                        "description": "Maximum opportunities to output.",
                        "default": 10
                    },
                    "subreddits": {
                        "title": "Subreddits",
                        "type": "array",
                        "description": "Public subreddits to search, without the r/ prefix. Presets provide defaults; explicit values override preset sources.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "keywords": {
                        "title": "Keywords",
                        "type": "array",
                        "description": "Pain-point and product-discovery phrases to search for. analysisGoal and market add transparent keyword hints.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "knownCompetitors": {
                        "title": "Known competitors",
                        "type": "array",
                        "description": "Optional tool/competitor names to track in entity recognition.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "timeRange": {
                        "title": "Time range",
                        "enum": [
                            "day",
                            "week",
                            "month",
                            "year",
                            "all"
                        ],
                        "type": "string",
                        "description": "Reddit search time range.",
                        "default": "month"
                    },
                    "sort": {
                        "title": "Search sort",
                        "enum": [
                            "relevance",
                            "new",
                            "top",
                            "comments"
                        ],
                        "type": "string",
                        "description": "Reddit search sort.",
                        "default": "relevance"
                    },
                    "aiProvider": {
                        "title": "AI provider",
                        "enum": [
                            "none",
                            "deepseek",
                            "openai",
                            "openrouter",
                            "anthropic",
                            "gemini",
                            "custom_openai_compatible"
                        ],
                        "type": "string",
                        "description": "Optional BYOK AI provider. Rules-based Reddit intelligence always runs first and does not require an AI key.",
                        "default": "none"
                    },
                    "aiMode": {
                        "title": "AI mode",
                        "enum": [
                            "off",
                            "polish",
                            "cluster",
                            "full"
                        ],
                        "type": "string",
                        "description": "polish adds final insight, cluster groups evidence signals into stronger opportunities, full does both.",
                        "default": "off"
                    },
                    "aiApiKey": {
                        "title": "AI API key",
                        "type": "string",
                        "description": "User-provided BYOK key. When set, model API costs are billed by the selected AI provider to the key owner."
                    },
                    "aiModel": {
                        "title": "AI model",
                        "type": "string",
                        "description": "Model name for the selected provider, for example deepseek-chat, gpt-4.1-mini, openai/gpt-4.1-mini, claude-3-5-haiku-latest, or gemini-2.0-flash."
                    },
                    "aiBaseUrl": {
                        "title": "AI base URL",
                        "type": "string",
                        "description": "Optional API base URL. Leave empty to use the selected provider default."
                    },
                    "maxAiInputChars": {
                        "title": "Max AI input characters",
                        "minimum": 1000,
                        "maximum": 100000,
                        "type": "integer",
                        "description": "Maximum characters sent to the AI provider. This limits call size but is not an exact billing cap.",
                        "default": 30000
                    },
                    "maxAiOutputTokens": {
                        "title": "Max AI output tokens",
                        "minimum": 500,
                        "maximum": 4000,
                        "type": "integer",
                        "description": "Maximum AI output tokens. This limits call size but is not an exact billing cap.",
                        "default": 1800
                    },
                    "trendMode": {
                        "title": "Trend mode",
                        "type": "boolean",
                        "description": "Add monitoring-style new/recurring/rising summaries.",
                        "default": false
                    },
                    "previousReport": {
                        "title": "Previous report JSON",
                        "type": "object",
                        "description": "Optional previous output JSON for monitoring comparisons."
                    },
                    "monitoringOnly": {
                        "title": "Monitoring only",
                        "type": "boolean",
                        "description": "Only keep opportunities that pass alertThreshold after analysis. Useful for scheduled alerts.",
                        "default": false
                    },
                    "alertThreshold": {
                        "title": "Alert threshold",
                        "minimum": 0,
                        "maximum": 100,
                        "type": "integer",
                        "description": "Opportunity score threshold for alert generation and monitoringOnly filtering.",
                        "default": 70
                    },
                    "dedupeAgainstPrevious": {
                        "title": "Dedupe against previous report",
                        "type": "boolean",
                        "description": "Compare current opportunities against previousReport when provided.",
                        "default": false
                    },
                    "includeComments": {
                        "title": "Include comments",
                        "type": "boolean",
                        "description": "Fetch and analyze public comments as supporting evidence. Comments improve evidence depth but can increase runtime and Apify platform usage.",
                        "default": true
                    },
                    "maxCommentsPerPost": {
                        "title": "Max comments per post",
                        "minimum": 0,
                        "maximum": 20,
                        "type": "integer",
                        "description": "Maximum comments to fetch per post. Default is 8; hard-capped at 20.",
                        "default": 8
                    },
                    "maxTotalComments": {
                        "title": "Max total comments",
                        "minimum": 0,
                        "maximum": 500,
                        "type": "integer",
                        "description": "Maximum comments to analyze across all posts. Default public run analyzes up to 150 comments; hard-capped at 500.",
                        "default": 150
                    },
                    "maxInputChars": {
                        "title": "Max input characters",
                        "minimum": 1000,
                        "maximum": 400000,
                        "type": "integer",
                        "description": "Maximum characters of combined post/comment text to analyze.",
                        "default": 200000
                    },
                    "analysisDepth": {
                        "title": "Analysis depth",
                        "enum": [
                            "quick",
                            "standard",
                            "deep"
                        ],
                        "type": "string",
                        "description": "Controls intended analysis depth. Deep mode is best with AI clustering enabled.",
                        "default": "standard"
                    },
                    "opportunityFocus": {
                        "title": "Opportunity focus",
                        "enum": [
                            "balanced",
                            "micro_saas",
                            "agency_services",
                            "content",
                            "competitor_switching"
                        ],
                        "type": "string",
                        "description": "Bias scoring toward a specific type of opportunity.",
                        "default": "balanced"
                    },
                    "reportType": {
                        "title": "Report type",
                        "enum": [
                            "micro_saas_ideas",
                            "competitor_complaints",
                            "content_ideas",
                            "feature_requests"
                        ],
                        "type": "string",
                        "description": "Opportunity lens for titles, suggested products, and scoring.",
                        "default": "micro_saas_ideas"
                    },
                    "minScore": {
                        "title": "Minimum opportunity score",
                        "minimum": 0,
                        "maximum": 100,
                        "type": "integer",
                        "description": "Filter out opportunities below this score.",
                        "default": 0
                    },
                    "minBuyingIntent": {
                        "title": "Minimum buying intent",
                        "minimum": 0,
                        "maximum": 100,
                        "type": "integer",
                        "description": "Filter out low-intent signals before opportunity clustering.",
                        "default": 0
                    },
                    "minDistinctEvidencePosts": {
                        "title": "Min distinct evidence posts",
                        "minimum": 1,
                        "maximum": 5,
                        "type": "integer",
                        "description": "Minimum distinct Reddit threads required before an opportunity is emitted.",
                        "default": 2
                    },
                    "dedupeStrength": {
                        "title": "Dedupe strength",
                        "enum": [
                            "low",
                            "medium",
                            "high"
                        ],
                        "type": "string",
                        "description": "How aggressively similar evidence signals are deduplicated.",
                        "default": "medium"
                    },
                    "excludeKeywords": {
                        "title": "Exclude keywords",
                        "type": "array",
                        "description": "Skip posts/comments that contain these terms.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "trackEntities": {
                        "title": "Track tools and competitors",
                        "type": "boolean",
                        "description": "Extract mentioned tools/competitors from public posts and comments.",
                        "default": true
                    },
                    "excludePromotionalPosts": {
                        "title": "Exclude promotional posts",
                        "type": "boolean",
                        "description": "Skip obvious launch/self-promotion posts as primary evidence while still analyzing their comments.",
                        "default": true
                    },
                    "outputFormat": {
                        "title": "Output format",
                        "enum": [
                            "json",
                            "markdown",
                            "csv"
                        ],
                        "type": "string",
                        "description": "Primary report format. Markdown is always generated as well.",
                        "default": "markdown"
                    },
                    "includeRawSignals": {
                        "title": "Include raw signals",
                        "type": "boolean",
                        "description": "Include sanitized extracted signal objects in output JSON.",
                        "default": false
                    },
                    "includeEvidenceTable": {
                        "title": "Include evidence table",
                        "type": "boolean",
                        "description": "Include a compact evidence table in JSON and Markdown.",
                        "default": true
                    },
                    "language": {
                        "title": "Report language",
                        "enum": [
                            "en",
                            "zh"
                        ],
                        "type": "string",
                        "description": "Language hint for summaries and AI output.",
                        "default": "en"
                    },
                    "reportTone": {
                        "title": "Report tone",
                        "enum": [
                            "operator",
                            "investor",
                            "founder",
                            "marketer"
                        ],
                        "type": "string",
                        "description": "Audience lens for report phrasing.",
                        "default": "operator"
                    },
                    "proxyType": {
                        "title": "Proxy type",
                        "enum": [
                            "auto",
                            "none",
                            "residential"
                        ],
                        "type": "string",
                        "description": "Use auto for direct local requests and Apify RESIDENTIAL proxy in cloud. Use none only for local debugging.",
                        "default": "auto"
                    },
                    "mode": {
                        "title": "Mode",
                        "enum": [
                            "reddit_discovery",
                            "analyze_dataset"
                        ],
                        "type": "string",
                        "description": "Use reddit_discovery for real public Reddit analysis. analyze_dataset is only for testing with Reddit-like JSON.",
                        "default": "reddit_discovery"
                    },
                    "posts": {
                        "title": "Posts",
                        "type": "array",
                        "description": "Reddit-like posts for analyze_dataset mode only."
                    },
                    "businessQuestion": {
                        "title": "Legacy business question",
                        "type": "string",
                        "description": "Backward-compatible alias. Prefer analysisGoal.",
                        "default": ""
                    },
                    "useAi": {
                        "title": "Legacy use AI",
                        "type": "boolean",
                        "description": "Backward-compatible alias accepted to prevent old inputs from failing. Prefer aiProvider and aiMode.",
                        "default": false
                    },
                    "useOwnerAiKey": {
                        "title": "Legacy owner AI key",
                        "type": "boolean",
                        "description": "Backward-compatible alias accepted to prevent old inputs from failing. Ignored in public runs; AI only uses aiApiKey.",
                        "default": false
                    },
                    "aiEnhancement": {
                        "title": "Legacy AI enhancement",
                        "type": "string",
                        "description": "Backward-compatible alias. Prefer aiProvider and aiMode.",
                        "default": "off"
                    },
                    "deepseekModel": {
                        "title": "Legacy DeepSeek model",
                        "type": "string",
                        "description": "Backward-compatible alias. Prefer aiModel.",
                        "default": ""
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
