{
  "data": {
    "id": "yIRmVL8pIqEFigLe1",
    "actId": "SG9Ngr95n5Hkk64vN",
    "userId": "IYgBDiXtHOijAaUel",
    "startedAt": "2026-11-09T06:09:15.396Z",
    "finishedAt": "2026-11-09T06:09:48.984Z",
    "status": "SUCCEEDED",
    "meta": {
      "origin": "CLI"
    },
    "stats": {
      "durationMillis": 33491,
      "runTimeSecs": 33.491,
      "computeUnits": 0.03721222222222222,
      "imageSizeBytes": 3119853568
    },
    "options": {
      "useCache": true,
      "betaPackages": false,
      "memoryMbytes": 4096,
      "diskMbytes": 8192
    },
    "createdByOrganizationMemberUserId": "QYo53EZkJk0prePWY",
    "exitCode": 0,
    "inputSchema": "{\n  \"title\": \"Reddit Keywords Pro Input\",\n  \"description\": \"Search Reddit by keywords with advanced filters, multi-source live/archive fallbacks, comment search, and only-new monitoring. Sources: Google Translate community partial, Reddit RSS, Redlib, Arctic Shift archive, optional user OAuth token.\",\n  \"type\": \"object\",\n  \"schemaVersion\": 1,\n  \"properties\": {\n    \"keywords\": {\n      \"title\": \"Keywords\",\n      \"type\": \"array\",\n      \"description\": \"List of keywords to search. Each runs as a separate search.\",\n      \"editor\": \"stringList\",\n      \"minItems\": 1,\n      \"prefill\": [\n        \"python programming\"\n      ],\n      \"default\": []\n    },\n    \"resultLimit\": {\n      \"title\": \"Result limit per keyword\",\n      \"type\": \"integer\",\n      \"description\": \"Max posts per keyword (1-1000). Default 100. Source caps are real: RSS 100/request with no pagination, Translate partial 24/page, Redlib 7/page, Arctic 100/page. Fewer results are reported as partial rather than silently truncated.\",\n      \"default\": 100,\n      \"minimum\": 1,\n      \"maximum\": 1000\n    },\n    \"sort\": {\n      \"title\": \"Sort by\",\n      \"type\": \"string\",\n      \"description\": \"How to sort the search results.\",\n      \"default\": \"relevance\",\n      \"enum\": [\n        \"relevance\",\n        \"hot\",\n        \"top\",\n        \"new\",\n        \"comments\"\n      ],\n      \"enumTitles\": [\n        \"Relevance\",\n        \"Hot\",\n        \"Top\",\n        \"New\",\n        \"Comments\"\n      ],\n      \"editor\": \"select\"\n    },\n    \"relativeWindow\": {\n      \"title\": \"Relative time window\",\n      \"type\": \"string\",\n      \"description\": \"Restrict results to a relative window. Maps to Reddit's t= and Arctic after= filters. Use all for no window.\",\n      \"default\": \"all\",\n      \"enum\": [\n        \"hour\",\n        \"day\",\n        \"week\",\n        \"month\",\n        \"year\",\n        \"all\"\n      ],\n      \"enumTitles\": [\n        \"Past hour\",\n        \"Past day\",\n        \"Past week\",\n        \"Past month\",\n        \"Past year\",\n        \"All time\"\n      ],\n      \"editor\": \"select\"\n    },\n    \"onlyNew\": {\n      \"title\": \"Only new since last run\",\n      \"type\": \"boolean\",\n      \"description\": \"Emit only records newer than the stored watermark for this monitorId. Requires a stable monitorId for scheduled runs.\",\n      \"default\": false,\n      \"prefill\": false\n    },\n    \"monitorId\": {\n      \"title\": \"Monitor ID\",\n      \"type\": \"string\",\n      \"description\": \"Key used to store the onlyNew watermark and seen IDs. Use the same value on every scheduled run of the same monitor.\",\n      \"editor\": \"textfield\"\n    },\n    \"searchComments\": {\n      \"title\": \"Search comments by keyword\",\n      \"type\": \"boolean\",\n      \"description\": \"Also search comment bodies via Redlib, with RSS parent-post discovery + hydration as fallback. Emits recordType=comment records.\",\n      \"default\": false,\n      \"prefill\": false\n    },\n    \"commentLimit\": {\n      \"title\": \"Comment limit per keyword\",\n      \"type\": \"integer\",\n      \"description\": \"Maximum matching comments per keyword when searchComments is enabled (1-1000).\",\n      \"default\": 50,\n      \"minimum\": 1,\n      \"maximum\": 1000\n    },\n    \"subredditFilter\": {\n      \"title\": \"Subreddit filter (allowlist)\",\n      \"type\": \"array\",\n      \"description\": \"Only emit posts/comments from these subreddits (with or without `r/`). Empty = all. When set, the engine prefers the subreddit-scoped Translate listing.\",\n      \"editor\": \"stringList\",\n      \"default\": []\n    },\n    \"subredditBlocklist\": {\n      \"title\": \"Subreddit blocklist\",\n      \"type\": \"array\",\n      \"description\": \"Drop posts/comments from these subreddits.\",\n      \"editor\": \"stringList\",\n      \"default\": []\n    },\n    \"minScore\": {\n      \"title\": \"Min score (filter)\",\n      \"type\": \"integer\",\n      \"description\": \"Drop records below this score. Applied only when the source exposes a live or explicitly timestamped score; otherwise the run reports metrics_unavailable instead of silently passing/dropping.\",\n      \"minimum\": -10000,\n      \"maximum\": 10000000\n    },\n    \"maxAgeDays\": {\n      \"title\": \"Max post age in days (filter)\",\n      \"type\": \"integer\",\n      \"description\": \"Drop records older than N days.\",\n      \"minimum\": 1,\n      \"maximum\": 36500\n    },\n    \"excludeNsfw\": {\n      \"title\": \"Exclude NSFW\",\n      \"type\": \"boolean\",\n      \"description\": \"Drop NSFW records. When the available source cannot expose a per-post NSFW flag, unknown rows are not emitted as safe; the run reports nsfw_status_unavailable.\",\n      \"default\": false,\n      \"prefill\": false\n    },\n    \"authorBlocklist\": {\n      \"title\": \"Author blocklist\",\n      \"type\": \"array\",\n      \"description\": \"Drop posts/comments by these usernames (case-insensitive).\",\n      \"editor\": \"stringList\",\n      \"default\": []\n    },\n    \"keywordRequireAll\": {\n      \"title\": \"Require all keywords (AND)\",\n      \"type\": \"boolean\",\n      \"description\": \"When multiple keywords are given, keep only records whose title/content contains ALL of them. Default: each keyword runs independently (OR).\",\n      \"default\": false,\n      \"prefill\": false\n    },\n    \"dateFrom\": {\n      \"title\": \"Date from (YYYY-MM-DD)\",\n      \"type\": \"string\",\n      \"description\": \"Drop records with `created_at` before this date (inclusive). Format: YYYY-MM-DD or ISO timestamp.\",\n      \"editor\": \"textfield\"\n    },\n    \"dateTo\": {\n      \"title\": \"Date to (YYYY-MM-DD)\",\n      \"type\": \"string\",\n      \"description\": \"Drop records with `created_at` after this date (inclusive end-of-day). Format: YYYY-MM-DD or ISO timestamp.\",\n      \"editor\": \"textfield\"\n    },\n    \"includeArchive\": {\n      \"title\": \"Include archive fallback\",\n      \"type\": \"boolean\",\n      \"description\": \"Use Arctic Shift for subreddit-scoped backfill and comment hydration when live sources fail. Archive metrics are snapshots, not live.\",\n      \"default\": true,\n      \"prefill\": true\n    },\n    \"redditOAuthToken\": {\n      \"title\": \"Optional Reddit OAuth token\",\n      \"type\": \"string\",\n      \"description\": \"Optional user-supplied OAuth Bearer token. Never required; when present, oauth.reddit.com is tried first for live metrics. The actor does not obtain tokens for you.\",\n      \"editor\": \"textfield\",\n      \"isSecret\": true\n    },\n    \"redlibInstances\": {\n      \"title\": \"Redlib instances\",\n      \"type\": \"array\",\n      \"description\": \"Optional Redlib hostnames to try in order (without https://). Defaults to safereddit.com, redlib.catsarch.com, redlib.perennialte.ch.\",\n      \"editor\": \"stringList\",\n      \"default\": []\n    },\n    \"resolveChallenges\": {\n      \"title\": \"Resolve Redlib challenges\",\n      \"type\": \"boolean\",\n      \"description\": \"Allow a best-effort Playwright browser solve when a Redlib instance returns an Anubis challenge. Disable to keep runs HTTP-only.\",\n      \"default\": true,\n      \"prefill\": true\n    },\n    \"proxyConfiguration\": {\n      \"title\": \"Proxy configuration\",\n      \"type\": \"object\",\n      \"description\": \"Optional Apify proxy configuration. Live HTML/Redlib/RSS availability varies by IP; residential proxies improve success on datacenter runs.\",\n      \"editor\": \"proxy\",\n      \"default\": {\n        \"useApifyProxy\": false\n      }\n    }\n  },\n  \"required\": [\n    \"keywords\"\n  ]\n}",
    "readme": "# Reddit Keyword Search with Advanced Filters\r\n\r\nSearch Reddit for one or more keywords and get back matching posts plus optional keyword-matched comments, narrowed down with subreddit allow/block lists, a minimum score, a relative or absolute date window, an NSFW toggle, and an author blocklist.\r\n\r\nReddit's anonymous old.reddit and public JSON endpoints are blocked, so this actor uses a multi-source transport with explicit provenance instead of pretending a blocked page was an empty successful run.\r\n\r\n## What this actor does\r\n\r\n- Runs a separate search for each keyword and tags every record with the keyword that found it\r\n- Uses a source cascade: Google Translate `community-more-posts` partial (subreddit-scoped live listing), Reddit RSS `search.rss?type=link` (global/subreddit search), Redlib (search, comment search, opaque cursors), Arctic Shift archive (backfill/hydration), and an optional user-supplied OAuth token\r\n- Adds `source`, `sourceUrl`, `retrievedAt`, `dataFreshness`, `scoreAsOf`, `upvoteRatioAsOf` and `archiveLagSeconds` to every record so live metrics are never confused with archive snapshots\r\n- Searches comment bodies with `searchComments` via Redlib, with RSS `type=comment` parent discovery plus hydration as fallback\r\n- Supports `onlyNew` + `monitorId` watermarks for scheduled monitoring, including relative windows (`hour` … `all`)\r\n- Applies subreddit, score, age, date range, NSFW, author and multi-keyword AND filters\r\n- Does not treat unknown NSFW flags or missing score metrics as safe/passing; it emits a typed status instead\r\n- De-duplicates posts by `t3_id` and comments by `t1_id` across keywords and sources, merging provenance and preferring live metrics over archive snapshots\r\n- Fails loudly when every source fails; a working source with zero matches emits `no_results`, not an error\r\n- **Empty fields are omitted:** never null, empty strings or empty arrays/objects\r\n\r\n## Output records\r\n\r\n### Post records (`recordType: \"post\"`)\r\n\r\nLegacy fields are preserved: `keyword`, `post_id`, `post_name`, `title`, `author`, `author_fullname`, `subreddit`, `subreddit_prefixed`, `content`, `score`, `num_comments`, `url`, `permalink`, `external_url`, `thumbnail_url`, `link_flair`, `created_utc`, `created_at`, `is_self`, `is_stickied`, `is_nsfw`, `is_spoiler`, `is_promoted`, `is_original_content`, `gilded_count`, `num_crossposts`, `crosspost_parent`, `post_hint`, `title_word_count`, `post_age_seconds`.\r\n\r\nAdded fields include: `keywords`, `upvote_ratio`, `source`, `sourceUrl`, `retrievedAt`, `dataFreshness`, `scoreAsOf`, `upvoteRatioAsOf`, `archiveLagSeconds`, `metricsAvailable`, `isNsfwKnown`, `isGallery`, `galleryItems`, `mediaUrls`, `videoUrl`, `removedByCategory`, `removalType`, `wasDeletedLater`, `subreddit_id`, `postType` and `provenance`.\r\n\r\n`subreddit` is the bare name (`Python`); `subreddit_prefixed` is `r/Python`. `domain` is the linked domain for link posts. Every pushed record also carries `scrapedAt` (the run timestamp) in addition to source provenance.\r\n\r\n### Comment records (`recordType: \"comment\"`)\r\n\r\n`keyword`, `keywords`, `commentId`, `comment_id`, `post_id`, `link_id`, `parentId`, `depth`, `body`, `author`, `author_fullname`, `subreddit`, `subreddit_prefixed`, `score`, `created_utc`, `created_at`, `permalink`, `isSubmitter`, `stickied`, `distinguished`, `scoreHidden`, `edited`, `collapsed`, `source`, `sourceUrl`, `retrievedAt`, `dataFreshness`, `scoreAsOf` and `provenance`.\r\n\r\n### Status/error records (`recordType: \"status\"` or `\"error\"`)\r\n\r\nEmitted when sources partially fail, are rate-limited, return no results, cannot supply a requested metric/NSFW flag, or when every source fails. They include `reason`, `message`, `keywords`, `sources`, `attemptedSources`, `counts`, `appliedFilters`, `sourceFailures` and `retryAfterSecs`.\r\n\r\nTyped reasons include: `invalid_input`, `all_sources_failed`, `transport_blocked`, `login_required`, `challenge_required`, `rate_limited`, `source_timeout`, `source_unavailable`, `metrics_unavailable`, `nsfw_status_unavailable`, `all_filters_dropped_results`, `partial_results` and `no_results`.\r\n\r\n## Input\r\n\r\n| Field | Type | Default | Description |\r\n|---|---|---|---|\r\n| `keywords` | array | – | Keywords to search; each runs as its own search (required) |\r\n| `resultLimit` | integer | `100` | Max posts per keyword (1-1000). Source caps are real: RSS 100/request, Translate 24/page, Redlib 7/page, Arctic 100/page |\r\n| `sort` | string | `relevance` | `relevance`, `hot`, `top`, `new`, or `comments` |\r\n| `relativeWindow` | string | `all` | `hour`, `day`, `week`, `month`, `year`, or `all` |\r\n| `onlyNew` | boolean | `false` | Emit only records newer than the stored watermark |\r\n| `monitorId` | string | – | Stable key for the onlyNew watermark/seen IDs |\r\n| `searchComments` | boolean | `false` | Search comment bodies and emit `recordType=comment` records |\r\n| `commentLimit` | integer | `50` | Max matching comments per keyword |\r\n| `subredditFilter` | array | `[]` | Only keep posts/comments from these subreddits (with or without `r/`) |\r\n| `subredditBlocklist` | array | `[]` | Drop posts/comments from these subreddits |\r\n| `minScore` | integer | – | Drop records below this score; skipped/reported when no reliable score is available |\r\n| `maxAgeDays` | integer | – | Drop records older than N days |\r\n| `excludeNsfw` | boolean | `false` | Drop NSFW records; unknown flags are reported, not silently passed |\r\n| `authorBlocklist` | array | `[]` | Drop these usernames (case-insensitive) |\r\n| `keywordRequireAll` | boolean | `false` | With multiple keywords, keep only records containing all of them |\r\n| `dateFrom` | string | – | Earliest `created_at` (inclusive, `YYYY-MM-DD` or ISO) |\r\n| `dateTo` | string | – | Latest `created_at` (inclusive end-of-day) |\r\n| `includeArchive` | boolean | `true` | Use Arctic Shift for subreddit backfill/comment hydration |\r\n| `redditOAuthToken` | secret | – | Optional user OAuth Bearer token; never required |\r\n| `redlibInstances` | array | `[]` | Optional Redlib hostnames in priority order |\r\n| `resolveChallenges` | boolean | `true` | Best-effort Playwright solve for Redlib Anubis challenges |\r\n| `proxyConfiguration` | object | – | Optional Apify proxy configuration |\r\n\r\n### Example: single keyword, top posts\r\n\r\n```json\r\n{\r\n  \"keywords\": [\"machine learning\"],\r\n  \"resultLimit\": 100,\r\n  \"sort\": \"top\",\r\n  \"relativeWindow\": \"month\"\r\n}\r\n```\r\n\r\n### Example: brand monitoring with only-new watermarks\r\n\r\n```json\r\n{\r\n  \"keywords\": [\"mybrand\", \"mybrand review\"],\r\n  \"resultLimit\": 100,\r\n  \"sort\": \"new\",\r\n  \"onlyNew\": true,\r\n  \"monitorId\": \"mybrand-daily\",\r\n  \"relativeWindow\": \"day\",\r\n  \"excludeNsfw\": true\r\n}\r\n```\r\n\r\n### Example: subreddit-scoped comments\r\n\r\n```json\r\n{\r\n  \"keywords\": [\"layoffs\"],\r\n  \"subredditFilter\": [\"technology\", \"cscareerquestions\"],\r\n  \"searchComments\": true,\r\n  \"commentLimit\": 50,\r\n  \"maxAgeDays\": 30,\r\n  \"resultLimit\": 50\r\n}\r\n```\r\n\r\n## Source behavior and limits\r\n\r\n- **Translate partial**: live, subreddit-scoped listing; 24 posts/page with opaque cursors; keyword matching is client-side on title/body. No per-post NSFW/locked/spoiler flags. All gallery images found in the post element are exposed through `mediaUrls`/`galleryItems`.\r\n- **Reddit RSS**: live global/subreddit search; max 100 entries per request, no pagination, roughly one request per minute per IP, and no score/comment/ratio metrics. Expected to be retired by Reddit.\r\n- **Redlib**: live third-party front-end; 7 posts / 10 comments per page with opaque cursors; Anubis challenges and upstream rate limits are common. This is the primary cookieless comment-body search. Default instances are `redlib-1.privadency.com` and `red.artemislena.eu`; override with `redlibInstances`.\r\n- **Arctic Shift**: archive, not a monitor. Scoped keyword searches may return HTTP 422 “Timeout”; score/comment/ratio values are snapshots (`dataFreshness` and `scoreAsOf` say which). Used for subreddit-scoped backfill/hydration and as the third comment-body rung via `comments/search?subreddit=...&body=...`.\r\n- **OAuth**: only when you supply a token; the actor never obtains one. `oauth.reddit.com` without a token returns a login/block page, so there is no fake “cookieless OAuth”.\r\n\r\n### Hard limits (2026-11)\r\n\r\n- **old.reddit.com**: anonymous requests redirect to a login wall (`/login/?reason=lor2`). The actor does not use old.reddit.\r\n- **Reddit RSS retirement**: RSS search is scheduled to stop on **2026-11-13**. Until then it is best-effort; afterwards global live search depends on Redlib and the intermittent Translate path.\r\n- **Global Arctic search**: `posts/search?query=` and `comments/search?body=` without `subreddit`, `author`, `link_id`, or `parent_id` return HTTP 400. Global keyword/comment search cannot use Arctic; only subreddit-scoped archive queries are supported.\r\n- **Arctic archive staleness**: scores, comment counts, upvote ratios and removal flags are snapshots, not live. Check `dataFreshness`, `scoreAsOf`, `upvoteRatioAsOf` and `archiveLagSeconds` before treating archive engagement as current; removed/deleted flags may lag live Reddit state.\r\n- **Legal/ToS**: Reddit requires approved API access and prohibits unapproved scraping and commercialization. Arctic Shift, Redlib and the Translate proxy are third-party services and do not create Reddit authorization. Get legal/product sign-off and provide provenance and removal handling; this actor is not affiliated with Reddit.\r\n\r\n## Use cases\r\n\r\n- **Brand and product monitoring** - track new Reddit discussions about a product, feature or competitor across subreddits.\r\n- **Community research** - collect posts and comment bodies for a niche to understand recurring questions, pain points and language.\r\n- **Trend discovery** - compare keyword volumes and engagement across time windows and sort orders.\r\n- **Support and risk triage** - find comments mentioning outages, billing issues or safety concerns and route them to the right team.\r\n- **Dataset building** - export provenance-rich posts/comments with live or archive freshness labels for analytics and NLP.\r\n\r\n## FAQ\r\n\r\n**Do I need a Reddit account or API credentials?**\r\nNo. The cookieless cascade works without credentials. An optional OAuth token improves live metrics if you have one.\r\n\r\n**Why are some posts missing `score` or `num_comments`?**\r\nRSS does not expose them. Records from RSS carry `metricsAvailable: false`; requesting a score filter causes a typed metrics status instead of silently passing rows.\r\n\r\n**Why did I get fewer results than `resultLimit`?**\r\nSource caps are real (RSS 100 with no pagination; Translate 24/page; Redlib 7/page; Arctic 100/page). A partial result is reported in the status record rather than silently hidden.\r\n\r\n**What does `dataFreshness` mean?**\r\n`live` for RSS/Translate/Redlib/OAuth; `archive_initial` or `archive_second_snapshot` for Arctic. Check `scoreAsOf`, `upvoteRatioAsOf` and `archiveLagSeconds` before treating archive engagement as current.\r\n\r\n**Is this affiliated with Reddit?**\r\nNo. This is an independent actor. Reddit's terms, the Responsible Builder Policy, and third-party service terms still apply; get legal/product approval for commercial or automated collection.\r\n",
    "actorDefinition": {
      "actorSpecification": 1,
      "name": "reddit-keyword-search-with-advanced-filters",
      "title": "Reddit Keyword Search with Advanced Filters",
      "description": "Search Reddit by keyword and filter the matches by subreddit, score, date range, NSFW status, and author before they reach your dataset.",
      "version": "1.0",
      "buildTag": "latest",
      "dockerfile": "./Dockerfile",
      "readme": "# Reddit Keyword Search with Advanced Filters\r\n\r\nSearch Reddit for one or more keywords and get back matching posts plus optional keyword-matched comments, narrowed down with subreddit allow/block lists, a minimum score, a relative or absolute date window, an NSFW toggle, and an author blocklist.\r\n\r\nReddit's anonymous old.reddit and public JSON endpoints are blocked, so this actor uses a multi-source transport with explicit provenance instead of pretending a blocked page was an empty successful run.\r\n\r\n## What this actor does\r\n\r\n- Runs a separate search for each keyword and tags every record with the keyword that found it\r\n- Uses a source cascade: Google Translate `community-more-posts` partial (subreddit-scoped live listing), Reddit RSS `search.rss?type=link` (global/subreddit search), Redlib (search, comment search, opaque cursors), Arctic Shift archive (backfill/hydration), and an optional user-supplied OAuth token\r\n- Adds `source`, `sourceUrl`, `retrievedAt`, `dataFreshness`, `scoreAsOf`, `upvoteRatioAsOf` and `archiveLagSeconds` to every record so live metrics are never confused with archive snapshots\r\n- Searches comment bodies with `searchComments` via Redlib, with RSS `type=comment` parent discovery plus hydration as fallback\r\n- Supports `onlyNew` + `monitorId` watermarks for scheduled monitoring, including relative windows (`hour` … `all`)\r\n- Applies subreddit, score, age, date range, NSFW, author and multi-keyword AND filters\r\n- Does not treat unknown NSFW flags or missing score metrics as safe/passing; it emits a typed status instead\r\n- De-duplicates posts by `t3_id` and comments by `t1_id` across keywords and sources, merging provenance and preferring live metrics over archive snapshots\r\n- Fails loudly when every source fails; a working source with zero matches emits `no_results`, not an error\r\n- **Empty fields are omitted:** never null, empty strings or empty arrays/objects\r\n\r\n## Output records\r\n\r\n### Post records (`recordType: \"post\"`)\r\n\r\nLegacy fields are preserved: `keyword`, `post_id`, `post_name`, `title`, `author`, `author_fullname`, `subreddit`, `subreddit_prefixed`, `content`, `score`, `num_comments`, `url`, `permalink`, `external_url`, `thumbnail_url`, `link_flair`, `created_utc`, `created_at`, `is_self`, `is_stickied`, `is_nsfw`, `is_spoiler`, `is_promoted`, `is_original_content`, `gilded_count`, `num_crossposts`, `crosspost_parent`, `post_hint`, `title_word_count`, `post_age_seconds`.\r\n\r\nAdded fields include: `keywords`, `upvote_ratio`, `source`, `sourceUrl`, `retrievedAt`, `dataFreshness`, `scoreAsOf`, `upvoteRatioAsOf`, `archiveLagSeconds`, `metricsAvailable`, `isNsfwKnown`, `isGallery`, `galleryItems`, `mediaUrls`, `videoUrl`, `removedByCategory`, `removalType`, `wasDeletedLater`, `subreddit_id`, `postType` and `provenance`.\r\n\r\n`subreddit` is the bare name (`Python`); `subreddit_prefixed` is `r/Python`. `domain` is the linked domain for link posts. Every pushed record also carries `scrapedAt` (the run timestamp) in addition to source provenance.\r\n\r\n### Comment records (`recordType: \"comment\"`)\r\n\r\n`keyword`, `keywords`, `commentId`, `comment_id`, `post_id`, `link_id`, `parentId`, `depth`, `body`, `author`, `author_fullname`, `subreddit`, `subreddit_prefixed`, `score`, `created_utc`, `created_at`, `permalink`, `isSubmitter`, `stickied`, `distinguished`, `scoreHidden`, `edited`, `collapsed`, `source`, `sourceUrl`, `retrievedAt`, `dataFreshness`, `scoreAsOf` and `provenance`.\r\n\r\n### Status/error records (`recordType: \"status\"` or `\"error\"`)\r\n\r\nEmitted when sources partially fail, are rate-limited, return no results, cannot supply a requested metric/NSFW flag, or when every source fails. They include `reason`, `message`, `keywords`, `sources`, `attemptedSources`, `counts`, `appliedFilters`, `sourceFailures` and `retryAfterSecs`.\r\n\r\nTyped reasons include: `invalid_input`, `all_sources_failed`, `transport_blocked`, `login_required`, `challenge_required`, `rate_limited`, `source_timeout`, `source_unavailable`, `metrics_unavailable`, `nsfw_status_unavailable`, `all_filters_dropped_results`, `partial_results` and `no_results`.\r\n\r\n## Input\r\n\r\n| Field | Type | Default | Description |\r\n|---|---|---|---|\r\n| `keywords` | array | – | Keywords to search; each runs as its own search (required) |\r\n| `resultLimit` | integer | `100` | Max posts per keyword (1-1000). Source caps are real: RSS 100/request, Translate 24/page, Redlib 7/page, Arctic 100/page |\r\n| `sort` | string | `relevance` | `relevance`, `hot`, `top`, `new`, or `comments` |\r\n| `relativeWindow` | string | `all` | `hour`, `day`, `week`, `month`, `year`, or `all` |\r\n| `onlyNew` | boolean | `false` | Emit only records newer than the stored watermark |\r\n| `monitorId` | string | – | Stable key for the onlyNew watermark/seen IDs |\r\n| `searchComments` | boolean | `false` | Search comment bodies and emit `recordType=comment` records |\r\n| `commentLimit` | integer | `50` | Max matching comments per keyword |\r\n| `subredditFilter` | array | `[]` | Only keep posts/comments from these subreddits (with or without `r/`) |\r\n| `subredditBlocklist` | array | `[]` | Drop posts/comments from these subreddits |\r\n| `minScore` | integer | – | Drop records below this score; skipped/reported when no reliable score is available |\r\n| `maxAgeDays` | integer | – | Drop records older than N days |\r\n| `excludeNsfw` | boolean | `false` | Drop NSFW records; unknown flags are reported, not silently passed |\r\n| `authorBlocklist` | array | `[]` | Drop these usernames (case-insensitive) |\r\n| `keywordRequireAll` | boolean | `false` | With multiple keywords, keep only records containing all of them |\r\n| `dateFrom` | string | – | Earliest `created_at` (inclusive, `YYYY-MM-DD` or ISO) |\r\n| `dateTo` | string | – | Latest `created_at` (inclusive end-of-day) |\r\n| `includeArchive` | boolean | `true` | Use Arctic Shift for subreddit backfill/comment hydration |\r\n| `redditOAuthToken` | secret | – | Optional user OAuth Bearer token; never required |\r\n| `redlibInstances` | array | `[]` | Optional Redlib hostnames in priority order |\r\n| `resolveChallenges` | boolean | `true` | Best-effort Playwright solve for Redlib Anubis challenges |\r\n| `proxyConfiguration` | object | – | Optional Apify proxy configuration |\r\n\r\n### Example: single keyword, top posts\r\n\r\n```json\r\n{\r\n  \"keywords\": [\"machine learning\"],\r\n  \"resultLimit\": 100,\r\n  \"sort\": \"top\",\r\n  \"relativeWindow\": \"month\"\r\n}\r\n```\r\n\r\n### Example: brand monitoring with only-new watermarks\r\n\r\n```json\r\n{\r\n  \"keywords\": [\"mybrand\", \"mybrand review\"],\r\n  \"resultLimit\": 100,\r\n  \"sort\": \"new\",\r\n  \"onlyNew\": true,\r\n  \"monitorId\": \"mybrand-daily\",\r\n  \"relativeWindow\": \"day\",\r\n  \"excludeNsfw\": true\r\n}\r\n```\r\n\r\n### Example: subreddit-scoped comments\r\n\r\n```json\r\n{\r\n  \"keywords\": [\"layoffs\"],\r\n  \"subredditFilter\": [\"technology\", \"cscareerquestions\"],\r\n  \"searchComments\": true,\r\n  \"commentLimit\": 50,\r\n  \"maxAgeDays\": 30,\r\n  \"resultLimit\": 50\r\n}\r\n```\r\n\r\n## Source behavior and limits\r\n\r\n- **Translate partial**: live, subreddit-scoped listing; 24 posts/page with opaque cursors; keyword matching is client-side on title/body. No per-post NSFW/locked/spoiler flags. All gallery images found in the post element are exposed through `mediaUrls`/`galleryItems`.\r\n- **Reddit RSS**: live global/subreddit search; max 100 entries per request, no pagination, roughly one request per minute per IP, and no score/comment/ratio metrics. Expected to be retired by Reddit.\r\n- **Redlib**: live third-party front-end; 7 posts / 10 comments per page with opaque cursors; Anubis challenges and upstream rate limits are common. This is the primary cookieless comment-body search. Default instances are `redlib-1.privadency.com` and `red.artemislena.eu`; override with `redlibInstances`.\r\n- **Arctic Shift**: archive, not a monitor. Scoped keyword searches may return HTTP 422 “Timeout”; score/comment/ratio values are snapshots (`dataFreshness` and `scoreAsOf` say which). Used for subreddit-scoped backfill/hydration and as the third comment-body rung via `comments/search?subreddit=...&body=...`.\r\n- **OAuth**: only when you supply a token; the actor never obtains one. `oauth.reddit.com` without a token returns a login/block page, so there is no fake “cookieless OAuth”.\r\n\r\n### Hard limits (2026-11)\r\n\r\n- **old.reddit.com**: anonymous requests redirect to a login wall (`/login/?reason=lor2`). The actor does not use old.reddit.\r\n- **Reddit RSS retirement**: RSS search is scheduled to stop on **2026-11-13**. Until then it is best-effort; afterwards global live search depends on Redlib and the intermittent Translate path.\r\n- **Global Arctic search**: `posts/search?query=` and `comments/search?body=` without `subreddit`, `author`, `link_id`, or `parent_id` return HTTP 400. Global keyword/comment search cannot use Arctic; only subreddit-scoped archive queries are supported.\r\n- **Arctic archive staleness**: scores, comment counts, upvote ratios and removal flags are snapshots, not live. Check `dataFreshness`, `scoreAsOf`, `upvoteRatioAsOf` and `archiveLagSeconds` before treating archive engagement as current; removed/deleted flags may lag live Reddit state.\r\n- **Legal/ToS**: Reddit requires approved API access and prohibits unapproved scraping and commercialization. Arctic Shift, Redlib and the Translate proxy are third-party services and do not create Reddit authorization. Get legal/product sign-off and provide provenance and removal handling; this actor is not affiliated with Reddit.\r\n\r\n## Use cases\r\n\r\n- **Brand and product monitoring** - track new Reddit discussions about a product, feature or competitor across subreddits.\r\n- **Community research** - collect posts and comment bodies for a niche to understand recurring questions, pain points and language.\r\n- **Trend discovery** - compare keyword volumes and engagement across time windows and sort orders.\r\n- **Support and risk triage** - find comments mentioning outages, billing issues or safety concerns and route them to the right team.\r\n- **Dataset building** - export provenance-rich posts/comments with live or archive freshness labels for analytics and NLP.\r\n\r\n## FAQ\r\n\r\n**Do I need a Reddit account or API credentials?**\r\nNo. The cookieless cascade works without credentials. An optional OAuth token improves live metrics if you have one.\r\n\r\n**Why are some posts missing `score` or `num_comments`?**\r\nRSS does not expose them. Records from RSS carry `metricsAvailable: false`; requesting a score filter causes a typed metrics status instead of silently passing rows.\r\n\r\n**Why did I get fewer results than `resultLimit`?**\r\nSource caps are real (RSS 100 with no pagination; Translate 24/page; Redlib 7/page; Arctic 100/page). A partial result is reported in the status record rather than silently hidden.\r\n\r\n**What does `dataFreshness` mean?**\r\n`live` for RSS/Translate/Redlib/OAuth; `archive_initial` or `archive_second_snapshot` for Arctic. Check `scoreAsOf`, `upvoteRatioAsOf` and `archiveLagSeconds` before treating archive engagement as current.\r\n\r\n**Is this affiliated with Reddit?**\r\nNo. This is an independent actor. Reddit's terms, the Responsible Builder Policy, and third-party service terms still apply; get legal/product approval for commercial or automated collection.\r\n",
      "input": {
        "title": "Reddit Keywords Pro Input",
        "description": "Search Reddit by keywords with advanced filters, multi-source live/archive fallbacks, comment search, and only-new monitoring. Sources: Google Translate community partial, Reddit RSS, Redlib, Arctic Shift archive, optional user OAuth token.",
        "type": "object",
        "schemaVersion": 1,
        "properties": {
          "keywords": {
            "title": "Keywords",
            "type": "array",
            "description": "List of keywords to search. Each runs as a separate search.",
            "editor": "stringList",
            "minItems": 1,
            "prefill": [
              "python programming"
            ],
            "default": []
          },
          "resultLimit": {
            "title": "Result limit per keyword",
            "type": "integer",
            "description": "Max posts per keyword (1-1000). Default 100. Source caps are real: RSS 100/request with no pagination, Translate partial 24/page, Redlib 7/page, Arctic 100/page. Fewer results are reported as partial rather than silently truncated.",
            "default": 100,
            "minimum": 1,
            "maximum": 1000
          },
          "sort": {
            "title": "Sort by",
            "type": "string",
            "description": "How to sort the search results.",
            "default": "relevance",
            "enum": [
              "relevance",
              "hot",
              "top",
              "new",
              "comments"
            ],
            "enumTitles": [
              "Relevance",
              "Hot",
              "Top",
              "New",
              "Comments"
            ],
            "editor": "select"
          },
          "relativeWindow": {
            "title": "Relative time window",
            "type": "string",
            "description": "Restrict results to a relative window. Maps to Reddit's t= and Arctic after= filters. Use all for no window.",
            "default": "all",
            "enum": [
              "hour",
              "day",
              "week",
              "month",
              "year",
              "all"
            ],
            "enumTitles": [
              "Past hour",
              "Past day",
              "Past week",
              "Past month",
              "Past year",
              "All time"
            ],
            "editor": "select"
          },
          "onlyNew": {
            "title": "Only new since last run",
            "type": "boolean",
            "description": "Emit only records newer than the stored watermark for this monitorId. Requires a stable monitorId for scheduled runs.",
            "default": false,
            "prefill": false
          },
          "monitorId": {
            "title": "Monitor ID",
            "type": "string",
            "description": "Key used to store the onlyNew watermark and seen IDs. Use the same value on every scheduled run of the same monitor.",
            "editor": "textfield"
          },
          "searchComments": {
            "title": "Search comments by keyword",
            "type": "boolean",
            "description": "Also search comment bodies via Redlib, with RSS parent-post discovery + hydration as fallback. Emits recordType=comment records.",
            "default": false,
            "prefill": false
          },
          "commentLimit": {
            "title": "Comment limit per keyword",
            "type": "integer",
            "description": "Maximum matching comments per keyword when searchComments is enabled (1-1000).",
            "default": 50,
            "minimum": 1,
            "maximum": 1000
          },
          "subredditFilter": {
            "title": "Subreddit filter (allowlist)",
            "type": "array",
            "description": "Only emit posts/comments from these subreddits (with or without `r/`). Empty = all. When set, the engine prefers the subreddit-scoped Translate listing.",
            "editor": "stringList",
            "default": []
          },
          "subredditBlocklist": {
            "title": "Subreddit blocklist",
            "type": "array",
            "description": "Drop posts/comments from these subreddits.",
            "editor": "stringList",
            "default": []
          },
          "minScore": {
            "title": "Min score (filter)",
            "type": "integer",
            "description": "Drop records below this score. Applied only when the source exposes a live or explicitly timestamped score; otherwise the run reports metrics_unavailable instead of silently passing/dropping.",
            "minimum": -10000,
            "maximum": 10000000
          },
          "maxAgeDays": {
            "title": "Max post age in days (filter)",
            "type": "integer",
            "description": "Drop records older than N days.",
            "minimum": 1,
            "maximum": 36500
          },
          "excludeNsfw": {
            "title": "Exclude NSFW",
            "type": "boolean",
            "description": "Drop NSFW records. When the available source cannot expose a per-post NSFW flag, unknown rows are not emitted as safe; the run reports nsfw_status_unavailable.",
            "default": false,
            "prefill": false
          },
          "authorBlocklist": {
            "title": "Author blocklist",
            "type": "array",
            "description": "Drop posts/comments by these usernames (case-insensitive).",
            "editor": "stringList",
            "default": []
          },
          "keywordRequireAll": {
            "title": "Require all keywords (AND)",
            "type": "boolean",
            "description": "When multiple keywords are given, keep only records whose title/content contains ALL of them. Default: each keyword runs independently (OR).",
            "default": false,
            "prefill": false
          },
          "dateFrom": {
            "title": "Date from (YYYY-MM-DD)",
            "type": "string",
            "description": "Drop records with `created_at` before this date (inclusive). Format: YYYY-MM-DD or ISO timestamp.",
            "editor": "textfield"
          },
          "dateTo": {
            "title": "Date to (YYYY-MM-DD)",
            "type": "string",
            "description": "Drop records with `created_at` after this date (inclusive end-of-day). Format: YYYY-MM-DD or ISO timestamp.",
            "editor": "textfield"
          },
          "includeArchive": {
            "title": "Include archive fallback",
            "type": "boolean",
            "description": "Use Arctic Shift for subreddit-scoped backfill and comment hydration when live sources fail. Archive metrics are snapshots, not live.",
            "default": true,
            "prefill": true
          },
          "redditOAuthToken": {
            "title": "Optional Reddit OAuth token",
            "type": "string",
            "description": "Optional user-supplied OAuth Bearer token. Never required; when present, oauth.reddit.com is tried first for live metrics. The actor does not obtain tokens for you.",
            "editor": "textfield",
            "isSecret": true
          },
          "redlibInstances": {
            "title": "Redlib instances",
            "type": "array",
            "description": "Optional Redlib hostnames to try in order (without https://). Defaults to safereddit.com, redlib.catsarch.com, redlib.perennialte.ch.",
            "editor": "stringList",
            "default": []
          },
          "resolveChallenges": {
            "title": "Resolve Redlib challenges",
            "type": "boolean",
            "description": "Allow a best-effort Playwright browser solve when a Redlib instance returns an Anubis challenge. Disable to keep runs HTTP-only.",
            "default": true,
            "prefill": true
          },
          "proxyConfiguration": {
            "title": "Proxy configuration",
            "type": "object",
            "description": "Optional Apify proxy configuration. Live HTML/Redlib/RSS availability varies by IP; residential proxies improve success on datacenter runs.",
            "editor": "proxy",
            "default": {
              "useApifyProxy": false
            }
          }
        },
        "required": [
          "keywords"
        ]
      },
      "output": {
        "actorOutputSchemaVersion": 1,
        "title": "Reddit Keyword Search with Advanced Filters Output",
        "description": "Reddit posts and optional keyword-matched comments with score, subreddit, author, timestamps, source provenance, and data freshness, after subreddit/score/date/NSFW/author filtering.",
        "properties": {
          "posts": {
            "type": "string",
            "title": "Reddit posts and comments",
            "description": "Dataset containing post records (recordType=post), optional comment records (recordType=comment), and typed status/error records.",
            "template": "{{links.apiDefaultDatasetUrl}}/items"
          }
        }
      },
      "storages": {
        "dataset": {
          "actorSpecification": 1,
          "views": {
            "overview": {
              "title": "Keyword Search Results",
              "transformation": {
                "fields": [
                  "recordType",
                  "keyword",
                  "title",
                  "author",
                  "subreddit_prefixed",
                  "score",
                  "num_comments",
                  "upvote_ratio",
                  "source",
                  "dataFreshness",
                  "url",
                  "created_at",
                  "link_flair"
                ]
              },
              "display": {
                "component": "table",
                "properties": {}
              }
            }
          }
        }
      },
      "environmentVariables": {}
    },
    "buildNumber": "1.0.4",
    "imageDigest": "866c04baed619425d0ad69ad67906607c183624273a3997aa855987ad6dbb4bd",
    "actVersion": {
      "sourceType": "SOURCE_FILES",
      "buildTag": "latest",
      "versionNumber": "1.0"
    },
    "usage": {
      "ACTOR_COMPUTE_UNITS": 0.03721222222222222
    }
  }
}