{
  "openapi": "3.0.1",
  "info": {
    "title": "Reddit Pain Finder",
    "description": "Discover real user pain points on Reddit. Reddit Pain Finder filters noise, classifies pain types (pricing, missing features, workflow friction, switching tools), and ranks discussions by priority. Works without API keys or with Reddit OAuth.",
    "version": "1.0",
    "x-build-id": "J156W2hdG4PkIZlGl"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/solutionssmart~reddit-pain-finder/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-solutionssmart-reddit-pain-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/solutionssmart~reddit-pain-finder/runs": {
      "post": {
        "operationId": "runs-sync-solutionssmart-reddit-pain-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/solutionssmart~reddit-pain-finder/run-sync": {
      "post": {
        "operationId": "run-sync-solutionssmart-reddit-pain-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": [
          "subreddits"
        ],
        "properties": {
          "forceMode": {
            "title": "⚙️ Data source mode",
            "enum": [
              "auto",
              "public",
              "api"
            ],
            "type": "string",
            "description": "Choose whether the Actor auto-detects the source, uses Reddit public endpoints only, or requires Reddit API credentials.",
            "default": "auto"
          },
          "redditClientId": {
            "title": "🆔 Reddit Client ID",
            "type": "string",
            "description": "Client ID for your Reddit app. Used only in API mode."
          },
          "redditClientSecret": {
            "title": "🔐 Reddit Client Secret",
            "type": "string",
            "description": "Client secret for your Reddit app. Used only in API mode."
          },
          "redditRefreshToken": {
            "title": "🔄 Reddit Refresh Token",
            "type": "string",
            "description": "Refresh token for your Reddit app. Used only in API mode."
          },
          "userAgent": {
            "title": "🪪 User-Agent",
            "type": "string",
            "description": "User-Agent header for Reddit requests.",
            "default": "apify:RedditPainFinder:v1.0 (public-rss-json-or-oauth)"
          },
          "apiRateLimitRps": {
            "title": "🚦 API rate limit (req/s)",
            "minimum": 0.1,
            "maximum": 10,
            "type": "number",
            "description": "Maximum requests per second in API mode.",
            "default": 1
          },
          "publicRateLimitRps": {
            "title": "🌐 Public rate limit (req/s)",
            "minimum": 0.1,
            "maximum": 10,
            "type": "number",
            "description": "Maximum requests per second in public mode.",
            "default": 1
          },
          "useApifyProxy": {
            "title": "🛡️ Use Apify Proxy",
            "type": "boolean",
            "description": "Route public-mode requests through Apify Proxy. Useful when Reddit returns 403 from datacenter IPs.",
            "default": false
          },
          "maxPostsTotal": {
            "title": "📦 Max posts total",
            "minimum": 1,
            "type": "integer",
            "description": "Optional cap across all subreddits after fetching and before dataset output."
          },
          "subreddits": {
            "title": "🎯 Subreddits",
            "minItems": 1,
            "uniqueItems": true,
            "type": "array",
            "description": "List of subreddit names without the r/ prefix.",
            "items": {
              "type": "string",
              "minLength": 1
            },
            "default": [
              "SaaS"
            ]
          },
          "sort": {
            "title": "↕️ Sort",
            "enum": [
              "new",
              "top"
            ],
            "type": "string",
            "description": "Choose whether to fetch the newest posts or top posts.",
            "default": "new"
          },
          "timeFilter": {
            "title": "🕒 Time filter",
            "enum": [
              "hour",
              "day",
              "week",
              "month",
              "year",
              "all"
            ],
            "type": "string",
            "description": "When using top posts, limit the ranking window.",
            "default": "week"
          },
          "limitPerSubreddit": {
            "title": "🔢 Limit per subreddit",
            "minimum": 1,
            "maximum": 100,
            "type": "integer",
            "description": "Maximum number of posts fetched per subreddit.",
            "default": 25
          },
          "afterISO": {
            "title": "📅 After date",
            "type": "string",
            "description": "Only keep posts created after this ISO date or timestamp."
          },
          "beforeISO": {
            "title": "📅 Before date",
            "type": "string",
            "description": "Only keep posts created before this ISO date or timestamp."
          },
          "includeComments": {
            "title": "💬 Include comments",
            "type": "boolean",
            "description": "Fetch and analyze comments. Turn this off for faster runs.",
            "default": false
          },
          "maxCommentsPerPost": {
            "title": "💬 Max comments per post",
            "minimum": 0,
            "maximum": 500,
            "type": "integer",
            "description": "Maximum comments fetched per post when comment collection is enabled.",
            "default": 50
          },
          "minUpvotes": {
            "title": "👍 Min upvotes",
            "minimum": 0,
            "type": "integer",
            "description": "Discard posts below this score.",
            "default": 0
          },
          "minComments": {
            "title": "🗨️ Min comments",
            "minimum": 0,
            "type": "integer",
            "description": "Discard posts below this comment count.",
            "default": 0
          },
          "minAgeMinutes": {
            "title": "⏳ Min post age (minutes)",
            "minimum": 0,
            "type": "integer",
            "description": "Skip very fresh posts in public mode. Useful when RSS items have zero engagement.",
            "default": 0
          },
          "keywords": {
            "title": "🔎 Strict filter keywords",
            "uniqueItems": true,
            "type": "array",
            "description": "Keywords, phrases, or regex patterns used for internal post filtering after scraping.",
            "items": {
              "type": "string",
              "minLength": 1
            }
          },
          "matchMode": {
            "title": "🧭 Keyword match mode",
            "enum": [
              "title",
              "body",
              "title_or_body"
            ],
            "type": "string",
            "description": "Choose whether keywords must match in the title, body, or either field.",
            "default": "title_or_body"
          },
          "matchType": {
            "title": "🧩 Keyword match type",
            "enum": [
              "contains",
              "exact",
              "regex"
            ],
            "type": "string",
            "description": "Choose how each keyword is interpreted.",
            "default": "contains"
          },
          "caseSensitive": {
            "title": "🔤 Case sensitive matching",
            "type": "boolean",
            "description": "When off, keywords and post text are normalized to lowercase before matching.",
            "default": false
          },
          "minMatchCount": {
            "title": "➕ Minimum keyword matches",
            "minimum": 1,
            "type": "integer",
            "description": "Minimum number of matches required for strict keyword filtering to pass.",
            "default": 1
          },
          "titleMustContainKeyword": {
            "title": "📰 Title must contain keyword",
            "type": "boolean",
            "description": "Reject posts unless the title matches at least one configured keyword.",
            "default": false
          },
          "outputMode": {
            "title": "📤 Keyword output mode",
            "enum": [
              "strict",
              "all",
              "scored"
            ],
            "type": "string",
            "description": "strict keeps only matched posts, all returns everything with match metadata, scored returns everything sorted by keyword score.",
            "default": "strict"
          },
          "customKeywords": {
            "title": "🩹 Additional pain keywords",
            "uniqueItems": true,
            "type": "array",
            "description": "Extra words or phrases for the pain analyzer. This is separate from the strict filter above.",
            "items": {
              "type": "string",
              "minLength": 1
            }
          },
          "postTypeWeights": {
            "title": "⚖️ Post type priority weights",
            "type": "object",
            "description": "Optional priority multipliers per post type. When set, these override the built-in promo and case-study weighting.",
            "properties": {
              "pain_report": {
                "title": "Pain report",
                "type": "number",
                "description": "Priority multiplier for pain_report items.",
                "minimum": 0
              },
              "question": {
                "title": "Question",
                "type": "number",
                "description": "Priority multiplier for question items.",
                "minimum": 0
              },
              "promo": {
                "title": "Promo",
                "type": "number",
                "description": "Priority multiplier for promo items.",
                "minimum": 0
              },
              "case_study": {
                "title": "Case study",
                "type": "number",
                "description": "Priority multiplier for case_study items.",
                "minimum": 0
              },
              "other": {
                "title": "Other",
                "type": "number",
                "description": "Priority multiplier for other items.",
                "minimum": 0
              }
            }
          },
          "includePostTypes": {
            "title": "✅ Include post types",
            "uniqueItems": true,
            "type": "array",
            "description": "Only output items whose postType is in this list. Leave empty to include all post types.",
            "items": {
              "type": "string",
              "enum": [
                "pain_report",
                "question",
                "promo",
                "case_study",
                "other"
              ]
            },
            "default": [
              "pain_report",
              "question"
            ]
          },
          "excludePostTypes": {
            "title": "🚫 Exclude post types",
            "uniqueItems": true,
            "type": "array",
            "description": "Always filter out these post types.",
            "items": {
              "type": "string",
              "enum": [
                "pain_report",
                "question",
                "promo",
                "case_study",
                "other"
              ]
            },
            "default": [
              "promo"
            ]
          },
          "promoDownrankFactor": {
            "title": "📉 Promo downrank factor",
            "minimum": 0,
            "maximum": 1,
            "type": "number",
            "description": "Multiplier for priorityScore when postType is promo.",
            "default": 0.25
          },
          "enablePriorityFloor": {
            "title": "🪜 Enable priority floor",
            "type": "boolean",
            "description": "Apply a tiered minimum priority for fresh posts with zero engagement and a classified pain type.",
            "default": true
          },
          "priorityFloorTiers": {
            "title": "🪜 Priority floor tiers",
            "type": "array",
            "description": "Optional custom floor tiers applied when engagement is zero. Highest minConfidence match wins.",
            "items": {
              "type": "object",
              "properties": {
                "minConfidence": {
                  "title": "Min confidence",
                  "type": "number",
                  "description": "Apply this floor when confidence is at least this value.",
                  "minimum": 0,
                  "maximum": 1
                },
                "floor": {
                  "title": "Floor",
                  "type": "integer",
                  "description": "Minimum priorityScore to apply for matching items.",
                  "minimum": 0,
                  "maximum": 100
                }
              },
              "required": [
                "minConfidence",
                "floor"
              ]
            },
            "default": [
              {
                "minConfidence": 0.7,
                "floor": 35
              },
              {
                "minConfidence": 0.6,
                "floor": 25
              },
              {
                "minConfidence": 0.5,
                "floor": 18
              }
            ]
          },
          "includeRaw": {
            "title": "🧾 Include raw fields",
            "type": "boolean",
            "description": "Include minimal raw fields in dataset items for debugging or downstream workflows.",
            "default": false
          },
          "debug": {
            "title": "🐞 Debug output",
            "type": "boolean",
            "description": "Include debug fields in each item and log strict-filter rejection reasons.",
            "default": false
          },
          "dedupeAcrossRuns": {
            "title": "♻️ Dedupe across runs",
            "type": "boolean",
            "description": "Skip posts already seen in previous runs using the STATE record in key-value store.",
            "default": false
          },
          "webhookUrl": {
            "title": "🔗 Webhook URL",
            "type": "string",
            "description": "Optional URL to POST batches of results."
          },
          "webhookBatchSize": {
            "title": "📬 Webhook batch size",
            "minimum": 1,
            "maximum": 500,
            "type": "integer",
            "description": "Number of items per webhook request.",
            "default": 50
          },
          "webhookMaxRetries": {
            "title": "🔁 Webhook max retries",
            "minimum": 1,
            "maximum": 10,
            "type": "integer",
            "description": "Maximum delivery attempts per webhook batch, including the first attempt.",
            "default": 5
          },
          "webhookRetryBaseMs": {
            "title": "⏱️ Webhook retry base delay (ms)",
            "minimum": 100,
            "maximum": 30000,
            "type": "integer",
            "description": "Base delay for exponential retry backoff.",
            "default": 500
          },
          "webhookTimeoutMs": {
            "title": "⏰ Webhook timeout (ms)",
            "minimum": 1000,
            "maximum": 120000,
            "type": "integer",
            "description": "HTTP timeout per webhook request attempt.",
            "default": 15000
          },
          "webhookAuthHeader": {
            "title": "🪪 Webhook auth header",
            "type": "string",
            "description": "Optional header name used with webhookAuthToken.",
            "default": "Authorization"
          },
          "webhookAuthToken": {
            "title": "🔐 Webhook auth token",
            "type": "string",
            "description": "Optional auth token value sent in the webhook auth header."
          },
          "webhookSecret": {
            "title": "🔏 Webhook HMAC secret",
            "type": "string",
            "description": "Optional secret used to sign payloads with HMAC SHA-256 in X-RedditPainFinder-Signature."
          },
          "webhookStoreFailedBatches": {
            "title": "🗂️ Store failed webhook batches",
            "type": "boolean",
            "description": "When true, failed batches are stored in WEBHOOK_FAILED_BATCHES for replay.",
            "default": true
          },
          "fetchPostBodiesInPublicMode": {
            "title": "Fetch public post bodies",
            "type": "boolean",
            "description": "When includeComments is off, fetch public thread JSON to enrich bodyText without collecting comments. Improves classification quality but adds extra public requests.",
            "default": true
          },
          "autoFallbackToApiOnPublic403": {
            "title": "Adaptive API fallback",
            "type": "boolean",
            "description": "When public Reddit returns repeated 403 responses and API credentials are available, retry the run in API mode automatically.",
            "default": true
          },
          "public403FallbackThreshold": {
            "title": "Public 403 fallback threshold",
            "minimum": 1,
            "maximum": 50,
            "type": "integer",
            "description": "Number of public 403 responses that triggers API fallback when adaptive fallback is enabled.",
            "default": 3
          },
          "publicThreadSuccessRateWarningThreshold": {
            "title": "Public thread success warning threshold",
            "minimum": 0,
            "maximum": 1,
            "type": "number",
            "description": "Warn when successful public thread fetches fall below this ratio. Useful for catching low-quality title-only runs caused by Reddit blocking.",
            "default": 0.6
          },
          "failOnLowPublicThreadSuccessRate": {
            "title": "Fail on low public thread coverage",
            "type": "boolean",
            "description": "When true, the run fails instead of returning degraded public results if thread fetch success falls below the warning threshold.",
            "default": false
          },
          "publicSubredditConcurrency": {
            "title": "Public subreddit concurrency",
            "minimum": 1,
            "maximum": 20,
            "type": "integer",
            "description": "Advanced override for concurrent subreddit fetches in public mode. Leave empty to auto-tune from publicRateLimitRps."
          },
          "publicThreadConcurrency": {
            "title": "Public thread concurrency",
            "minimum": 1,
            "maximum": 20,
            "type": "integer",
            "description": "Advanced override for concurrent public thread or body fetches. Leave empty to auto-tune."
          },
          "apiSubredditConcurrency": {
            "title": "API subreddit concurrency",
            "minimum": 1,
            "maximum": 20,
            "type": "integer",
            "description": "Advanced override for concurrent subreddit fetches in API mode. Leave empty to auto-tune from apiRateLimitRps."
          },
          "apiCommentConcurrency": {
            "title": "API comment concurrency",
            "minimum": 1,
            "maximum": 20,
            "type": "integer",
            "description": "Advanced override for concurrent API comment fetches. Leave empty to auto-tune."
          }
        }
      },
      "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
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}