{
  "data": {
    "id": "aXR6xVwyCStLrYveu",
    "actId": "hzfhxZwMsIQ2crhb4",
    "userId": "NL4ThgItAwaoBlffQ",
    "startedAt": "2026-10-23T00:06:58.920Z",
    "finishedAt": "2026-10-23T00:08:01.381Z",
    "status": "SUCCEEDED",
    "meta": {
      "origin": "CLI"
    },
    "stats": {
      "durationMillis": 62372,
      "runTimeSecs": 62.372,
      "computeUnits": 0.06930222222222222,
      "imageSizeBytes": 1238167552
    },
    "options": {
      "useCache": true,
      "betaPackages": false,
      "memoryMbytes": 4096,
      "diskMbytes": 8192
    },
    "createdByOrganizationMemberUserId": "zifX0mkAFMHe8CatY",
    "exitCode": 0,
    "inputSchema": "{\n  \"title\": \"Reddit Comment Scraper Input\",\n  \"type\": \"object\",\n  \"schemaVersion\": 1,\n  \"properties\": {\n    \"postUrls\": {\n      \"title\": \"Reddit Post URLs\",\n      \"type\": \"array\",\n      \"description\": \"Post URLs, comment URLs, share links or bare post IDs. Accepted forms: full post URL (https://www.reddit.com/r/x/comments/1abc/title/), short URL (https://www.reddit.com/comments/1abc or with ?sort=top), comment URL (.../comments/1abc/title/comment/xyz/), mobile share link (https://www.reddit.com/r/x/s/token), bare post id (1abc123) or t3_1abc123.\",\n      \"editor\": \"stringList\",\n      \"example\": [\n        \"https://www.reddit.com/r/programming/comments/1vqukkf/nothing_like_a_monday_morning_github_outage/\",\n        \"https://www.reddit.com/comments/1tlh5aj\"\n      ],\n      \"prefill\": [\n        \"https://www.reddit.com/r/programming/comments/1vqukkf/\"\n      ],\n      \"minItems\": 1\n    },\n    \"maxComments\": {\n      \"title\": \"Maximum comments per post\",\n      \"type\": \"integer\",\n      \"description\": \"Maximum number of comments to scrape from each post (including nested replies).\",\n      \"minimum\": 1,\n      \"maximum\": 100000,\n      \"default\": 100,\n      \"prefill\": 30,\n      \"example\": 100\n    },\n    \"commentSort\": {\n      \"title\": \"Sort comments by\",\n      \"type\": \"string\",\n      \"description\": \"How to sort the comment threads.\",\n      \"enum\": [\n        \"confidence\",\n        \"top\",\n        \"new\",\n        \"controversial\",\n        \"old\",\n        \"qa\"\n      ],\n      \"default\": \"confidence\",\n      \"prefill\": \"confidence\",\n      \"enumTitles\": [\n        \"Best (Confidence)\",\n        \"Top\",\n        \"New\",\n        \"Controversial\",\n        \"Old\",\n        \"Q&A\"\n      ]\n    },\n    \"includePost\": {\n      \"title\": \"Include the parent post record\",\n      \"type\": \"boolean\",\n      \"description\": \"When enabled, the parent post metadata is emitted as the first record of each thread.\",\n      \"default\": false,\n      \"prefill\": false\n    },\n    \"postedAfter\": {\n      \"title\": \"Comments created on or after (YYYY-MM-DD)\",\n      \"type\": \"string\",\n      \"description\": \"Only keep comments created on or after this date (UTC).\",\n      \"editor\": \"datepicker\",\n      \"example\": \"2025-01-01\"\n    },\n    \"postedBefore\": {\n      \"title\": \"Comments created on or before (YYYY-MM-DD)\",\n      \"type\": \"string\",\n      \"description\": \"Only keep comments created on or before this date (interpreted as the end of that day, UTC).\",\n      \"editor\": \"datepicker\",\n      \"example\": \"2025-12-31\"\n    },\n    \"minDepth\": {\n      \"title\": \"Minimum reply depth\",\n      \"type\": \"integer\",\n      \"description\": \"Only keep comments nested at or deeper than this depth (0 = keep everything, 1 = drop top-level comments and keep only replies, 2 = keep only replies-to-replies and deeper, etc.). Leave empty for no limit.\",\n      \"minimum\": 0,\n      \"maximum\": 100,\n      \"example\": 1\n    },\n    \"maxDepth\": {\n      \"title\": \"Maximum reply depth\",\n      \"type\": \"integer\",\n      \"description\": \"Only keep comments nested at or above this depth (0 = top-level comments only, 1 = top-level + first-level replies, etc.). Leave empty for no limit.\",\n      \"minimum\": 0,\n      \"maximum\": 100,\n      \"example\": 2\n    },\n    \"minCommentScore\": {\n      \"title\": \"Minimum comment score\",\n      \"type\": \"integer\",\n      \"description\": \"Only keep comments with a score (upvotes minus downvotes) at or above this value. Leave empty for no limit.\",\n      \"minimum\": -1000000,\n      \"maximum\": 1000000,\n      \"example\": 1\n    },\n    \"maxCommentScore\": {\n      \"title\": \"Maximum comment score\",\n      \"type\": \"integer\",\n      \"description\": \"Only keep comments with a score (upvotes minus downvotes) at or below this value. Leave empty for no limit.\",\n      \"minimum\": -1000000,\n      \"maximum\": 1000000,\n      \"example\": 1000\n    },\n    \"onlyOP\": {\n      \"title\": \"Only original poster's comments\",\n      \"type\": \"boolean\",\n      \"description\": \"When enabled, only comments written by the post's original author are kept.\",\n      \"default\": false,\n      \"prefill\": false\n    },\n    \"excludeDeletedRemoved\": {\n      \"title\": \"Exclude deleted/removed comments\",\n      \"type\": \"boolean\",\n      \"description\": \"When enabled, comments that Reddit marks as deleted or author-deleted are dropped (no real content left to scrape).\",\n      \"default\": false,\n      \"prefill\": false\n    },\n    \"excludeCollapsed\": {\n      \"title\": \"Exclude collapsed comments\",\n      \"type\": \"boolean\",\n      \"description\": \"When enabled, comments Reddit collapses by default (low score or crowd control) are dropped.\",\n      \"default\": false,\n      \"prefill\": false\n    },\n    \"authors\": {\n      \"title\": \"Filter by comment author(s)\",\n      \"type\": \"array\",\n      \"description\": \"Only keep comments written by these Reddit usernames (case-insensitive). Leave empty to keep comments from all authors.\",\n      \"editor\": \"stringList\",\n      \"example\": [\n        \"spez\"\n      ]\n    },\n    \"excludeAuthors\": {\n      \"title\": \"Exclude comment author(s)\",\n      \"type\": \"array\",\n      \"description\": \"Drop comments written by these Reddit usernames (case-insensitive), e.g. bots like AutoModerator. Leave empty to keep comments from all authors. Applied after `authors` (an author listed in both is excluded).\",\n      \"editor\": \"stringList\",\n      \"example\": [\n        \"AutoModerator\"\n      ]\n    },\n    \"keywords\": {\n      \"title\": \"Filter by keyword(s) in comment body\",\n      \"type\": \"array\",\n      \"description\": \"Only keep comments whose body contains at least one of these keywords or phrases (case-insensitive substring match). Leave empty to keep all comments.\",\n      \"editor\": \"stringList\",\n      \"example\": [\n        \"thanks\",\n        \"great point\"\n      ]\n    },\n    \"excludeKeywords\": {\n      \"title\": \"Exclude keyword(s) in comment body\",\n      \"type\": \"array\",\n      \"description\": \"Drop comments whose body contains any of these keywords or phrases (case-insensitive substring match), e.g. spam/boilerplate phrases. Applied after `keywords` (a keyword listed in both still excludes the comment). Leave empty to keep all comments.\",\n      \"editor\": \"stringList\",\n      \"example\": [\n        \"I am a bot\",\n        \"check out my\"\n      ]\n    },\n    \"minAwards\": {\n      \"title\": \"Minimum total awards received\",\n      \"type\": \"integer\",\n      \"description\": \"Only keep comments with at least this many total awards. Leave empty for no limit.\",\n      \"minimum\": 0,\n      \"maximum\": 1000000,\n      \"example\": 1\n    },\n    \"minCommentLength\": {\n      \"title\": \"Minimum comment length (characters)\",\n      \"type\": \"integer\",\n      \"description\": \"Only keep comments whose body text is at least this many characters long. Useful for filtering out one-word/emoji-only noise when building NLP datasets. Leave empty for no limit.\",\n      \"minimum\": 0,\n      \"maximum\": 10000,\n      \"example\": 20\n    },\n    \"maxCommentLength\": {\n      \"title\": \"Maximum comment length (characters)\",\n      \"type\": \"integer\",\n      \"description\": \"Only keep comments whose body text is at most this many characters long (Reddit's hard cap is 10,000 characters). Leave empty for no limit.\",\n      \"minimum\": 0,\n      \"maximum\": 10000,\n      \"example\": 2000\n    },\n    \"focusOnTargetComment\": {\n      \"title\": \"Focus on the specific comment in the URL\",\n      \"type\": \"boolean\",\n      \"description\": \"When a `postUrls` item is a specific comment permalink, return that comment plus its ancestor chain (see `commentContext`). Works for deeply nested targets; the target must still be publicly visible. Has no effect on plain post URLs.\",\n      \"default\": false,\n      \"prefill\": false\n    },\n    \"commentContext\": {\n      \"title\": \"Ancestor context depth (mode = focusOnTargetComment)\",\n      \"type\": \"integer\",\n      \"description\": \"How many levels of parent comments above the target comment to include when `focusOnTargetComment` is enabled (matches Reddit's own comment-permalink page). Ignored otherwise.\",\n      \"minimum\": 0,\n      \"maximum\": 8,\n      \"default\": 3,\n      \"prefill\": 3\n    }\n  },\n  \"required\": [\n    \"postUrls\"\n  ]\n}",
    "readme": "# Reddit Comment Thread Scraper with Score & Keyword Filters\n\nExport Reddit comment threads — top-level comments and nested replies, including branches hidden behind \"load more replies\" and \"continue this thread\" — as flat, structured rows. Paste post links (or comment links), choose a sort, and get author, text, score, timestamps, reply depth, parent relationships, and moderation/deletion flags. No Reddit account, API key, or login required.\n\n## What this actor does\n\n- **Full thread reconstruction** — follows Reddit's own nested reply loaders and continue-thread pages until `maxComments` is reached or Reddit stops serving comments to logged-out readers\n- **6 comment sorts** — Best, Top, New, Controversial, Old, and Q&A, matching Reddit's UI\n- **Comment permalink focus** — paste a comment permalink with `focusOnTargetComment` enabled to get that comment plus its ancestor chain, including deeply nested targets\n- **Rich client-side filtering** — date window, reply-depth range, score range, author allow/deny, keyword include/exclude, length bounds, award count, OP-only, deleted/removed, collapsed\n- **Optional parent post record** — title, author, score, ratio, timestamp, subreddit, and text/link metadata when `includePost` is on\n- **Flexible URL input** — post URLs, comment permalinks, share links, `redd.it` short links, bare post IDs\n- **Omit-empty output** — a field appears only when Reddit actually exposes a value for it\n\n## Output per comment\n\n- `dataType` — `\"comment\"`\n- `comment_id`, `comment_name` — bare id and Reddit fullname (`t1_...`)\n- `post_id`, `post_url`, `post_title`, `link_id` — parent thread context\n- `permalink` — direct comment link\n- `author` — username (`[deleted]` when the account is gone)\n- `avatarUrl` — author avatar image, when available\n- `body` — comment text as readable plain text (block breaks preserved)\n- `body_html` — comment HTML from Reddit's renderer\n- `score` — net score (upvotes minus downvotes) at crawl time\n- `created_utc`, `created_at` — posted time (epoch and ISO-8601 UTC)\n- `edited`, `edited_at` — whether and when the comment was edited\n- `depth` — nesting level, `0` = top-level reply to the post\n- `position` — the comment's position among its siblings\n- `parentPositions` — the position path from the thread root. The prefix can be incomplete for branches reached through a \"continue this thread\" page; use `parent_id` for exact parentage\n- `parent_id`, `parent_kind` — direct parent fullname (`t3_...` for top-level or `t1_...` for a reply) and kind\n- `is_op` — author is the post's original poster\n- `collapsed` — Reddit collapses the comment by default\n- `isCommentDeleted`, `isAuthorDeleted` — comment content / author account was deleted\n- `contentType` — `text`, `image`, or `giphy`\n- `isHighlighted`, `hasPaidAward` — Reddit highlight/paid-award markers when present\n- `total_awards_received` — total award count (0 when none)\n- `subreddit`, `subreddit_prefixed`, `subreddit_id` — community context\n- `crawled_at` — when this record was fetched (ISO-8601)\n- `source` — `\"html\"`\n\nA rare deleted-comment placeholder that Reddit no longer gives an id for has no `comment_id`; its `isCommentDeleted`/`isAuthorDeleted` flags and position path still identify it. Its `parent_id` falls back to the post, so use the position path for its place in the tree.\n\nFields Reddit does not expose to logged-out readers are omitted: comment `author_id`, author flair fields, `ups`/`downs`, `score_hidden`, `controversiality`, `distinguished`, comment-level `stickied`/`locked`/`archived`, `collapsed_reason*`, legacy `gilded`, the award breakdown, and `removed_by_category`. See **Limitations** below.\n\n## Output per post\n\nEmitted once per post as the first record when `includePost` is on. Comment filters do not apply to it.\n\n- `dataType` — `\"post\"`\n- `post_id`, `post_name`, `title`, `author`, `author_id`\n- `subreddit`, `subreddit_prefixed`, `subreddit_id`\n- `url`, `permalink` — canonical reddit.com link\n- `content_url` — external destination for link posts\n- `domain`, `post_type`, `is_self`\n- `content`, `content_html` — self-post body\n- `score`, `upvote_ratio`, `num_comments`\n- `created_utc`, `created_at`\n- `language`\n- `is_nsfw`, `is_stickied`, `is_locked`\n- `total_awards_received`\n- `crawled_at`, `source`\n\nPost fields that require Reddit's legacy API data (flair, media/gallery URLs, poll data, crosspost counts, removal category, ups/downs, edited timestamp) are not available to logged-out readers and are omitted.\n\n## Input\n\n| Field | Type | Default | Description |\n|---|---|---|---|\n| `postUrls` | array | – (required) | Post URLs, comment URLs, share links, `redd.it` short links, or bare post IDs |\n| `maxComments` | integer | `100` | Maximum comments to keep per post, after filters (1–100,000) |\n| `commentSort` | select | `confidence` | `confidence` (Best), `top`, `new`, `controversial`, `old`, `qa` |\n| `includePost` | boolean | `false` | Emit the parent post as the first record of each thread |\n| `postedAfter` | date | – | Keep comments created on/after this date (UTC) |\n| `postedBefore` | date | – | Keep comments created on/before this date (UTC; a bare date means end of day) |\n| `minDepth` | integer | – | Keep comments at or deeper than this nesting level (0–100) |\n| `maxDepth` | integer | – | Keep comments at or above this nesting level (0–100) |\n| `minCommentScore` | integer | – | Keep comments with score ≥ this value |\n| `maxCommentScore` | integer | – | Keep comments with score ≤ this value |\n| `onlyOP` | boolean | `false` | Keep only the original poster's comments |\n| `excludeDeletedRemoved` | boolean | `false` | Drop comments Reddit marks deleted or author-deleted |\n| `excludeCollapsed` | boolean | `false` | Drop comments Reddit collapses by default |\n| `authors` | array | – | Keep only these usernames (case-insensitive) |\n| `excludeAuthors` | array | – | Drop these usernames (case-insensitive), e.g. bots |\n| `keywords` | array | – | Keep comments containing at least one keyword/phrase |\n| `excludeKeywords` | array | – | Drop comments containing any keyword/phrase |\n| `minAwards` | integer | – | Keep comments with at least this many total awards |\n| `minCommentLength` | integer | – | Minimum body length in characters (0–10,000) |\n| `maxCommentLength` | integer | – | Maximum body length in characters (0–10,000) |\n| `focusOnTargetComment` | boolean | `false` | For a comment permalink, return the target plus its ancestor chain. Works for deeply nested targets; the target must still be publicly visible |\n| `commentContext` | integer | `3` | Ancestor levels above the target comment when Reddit returns the focus view (0–8) |\n\n**Accepted `postUrls` forms:**\n- Full post URL: `https://www.reddit.com/r/programming/comments/1abc123/some_title/`\n- Short URL: `https://www.reddit.com/comments/1abc123` (also with `?sort=top`)\n- Comment URL: `https://www.reddit.com/r/x/comments/1abc123/title/comment/xyz987/`\n- Share link: `https://www.reddit.com/r/x/s/<token>`\n- Short link: `https://redd.it/1abc123`\n- Bare post ID: `1abc123` or `t3_1abc123`\n- `old.reddit.com` URLs also work\n\nMalformed items (empty strings, garbage URLs, non-strings) are skipped without failing the run.\n\n**Removed inputs:** `distinguishedFilter`, `controversialOnly`, and `excludeStickied` were removed because Reddit no longer exposes those markers to logged-out readers. Old saved tasks that still send them keep running; the actor logs one warning and ignores them.\n\n### Example: scrape a whole thread sorted by Top\n\n```json\n{\n  \"postUrls\": [\"https://www.reddit.com/r/technology/comments/1abc123/some_title/\"],\n  \"maxComments\": 500,\n  \"commentSort\": \"top\",\n  \"includePost\": true\n}\n```\n\n### Example: recent, high-signal comments only\n\n```json\n{\n  \"postUrls\": [\"https://www.reddit.com/r/investing/comments/1abc123/some_title/\"],\n  \"maxComments\": 1000,\n  \"postedAfter\": \"2026-06-01\",\n  \"minCommentScore\": 5,\n  \"excludeDeletedRemoved\": true\n}\n```\n\n### Example: NLP dataset — clean comment bodies\n\n```json\n{\n  \"postUrls\": [\n    \"https://www.reddit.com/r/AskReddit/comments/1abc123/some_title/\",\n    \"https://www.reddit.com/r/AskReddit/comments/1def456/other_title/\"\n  ],\n  \"maxComments\": 5000,\n  \"excludeDeletedRemoved\": true,\n  \"excludeAuthors\": [\"AutoModerator\"],\n  \"minCommentLength\": 20,\n  \"excludeKeywords\": [\"I am a bot\"]\n}\n```\n\n### Example: jump straight to one comment\n\n```json\n{\n  \"postUrls\": [\"https://www.reddit.com/r/x/comments/1abc123/title/comment/xyz987/\"],\n  \"focusOnTargetComment\": true,\n  \"commentContext\": 5\n}\n```\n\n## Use cases\n\n- **Sentiment & NLP datasets** — build large, structured corpora from real discussions\n- **Market & product research** — read what customers say in the replies, not just the top comment\n- **Community & moderation analysis** — study conversation depth, controversy, and OP engagement\n- **Launch monitoring** — track reaction to announcements across subreddits on a schedule\n- **Academic & social research** — export discussion trees for network and discourse analysis\n- **Support mining** — surface recurring complaints buried deep in a thread\n\n## FAQ\n\n**Do I need a Reddit account, API key, or login?**\nNo. The actor reads Reddit's public pages anonymously and stores no credentials.\n\n**Why are some fields missing compared with older versions?**\nReddit stopped serving its anonymous JSON endpoints in 2026 and does not render author IDs, vote splits, flair, moderator badges, or award details to logged-out readers. The actor emits every field the public pages still expose and omits the rest instead of inventing values.\n\n**Does it really capture the whole thread?**\nUp to `maxComments`, yes. The actor follows Reddit's nested reply loaders and continue-thread pages until the thread is exhausted or your limit is reached. Reddit's displayed comment count includes deleted and removed comments, so a complete export can be smaller than that number.\n\n**What is the difference between `parent_id` and `link_id`?**\n`link_id` always points to the parent post (`t3_...`). `parent_id` points to the comment's direct parent — the post for a top-level comment or another comment for a reply. `parent_kind` tells you which, and `parentPositions` gives the position path (with the continue-page caveat above).\n\n**How do I scrape just one comment instead of the whole thread?**\nPaste its permalink into `postUrls` and enable `focusOnTargetComment`. You get that comment plus up to `commentContext` levels of parent comments. The target must still be publicly visible; deleted/removed targets return no records.\n\n**Why does a deleted comment still appear?**\nDeleted comments are part of the thread structure. They carry `isCommentDeleted` / `isAuthorDeleted` and usually no body; enable `excludeDeletedRemoved` to drop them.\n\n**What happens for a private subreddit, banned post, or deleted post?**\nThe actor returns 0 records for that URL and continues with the other URLs. A status message explains when nothing could be read.\n\n**Do I need to configure a proxy?**\nNo. The actor selects the network route it needs automatically.\n\n**Is this affiliated with Reddit?**\nNo. This is an independent third-party actor that reads public web pages; it is not endorsed by or affiliated with Reddit, Inc.\n\n## Cost\n\nMeasured all-in run cost is roughly **$0.72–1.58 per 1,000 comments**, depending on thread depth, how many continue-thread branches are loaded, and how many deleted comments Reddit still counts. Small or heavily filtered runs have a higher fixed cost per 1,000. Store pricing is set separately.\n\n## Limitations\n\n- Reddit's own displayed comment count includes deleted, removed, and filtered comments that are not served to logged-out readers. The actor reports what is actually served.\n- Comment author IDs, vote up/down splits, flair, moderator/admin badges, controversy flags, award names/icons, and removal categories are not exposed anonymously. They are omitted rather than guessed. (The post record still carries the post author's ID when Reddit renders it.)\n- Some comments arrive from Reddit's lazy-loading fragments with `body_html` but without a plain-text `body` (and occasionally without either) even though they are not deleted. Use `body_html` as a fallback; the omission rate measured in cloud runs was ~15–18%.\n- `parentPositions` can lose the continue-target's position for branches loaded through \"continue this thread\" pages, so paths for those descendants may not match the tree exactly. `parent_id` remains correct.\n- Deleted-comment placeholders that Reddit renders without an id may carry the post as `parent_id` even when nested; their position path is the only tree hint for those rows.\n- Very large threads take longer because every hidden branch is loaded through Reddit's own pagination; there is no branch pruning, so cost scales with the served tree as well as `maxComments`.\n- The parent post record is intentionally thin: media, gallery, poll, crosspost, and flair details are not available on the public post page.\n",
    "actorDefinition": {
      "actorSpecification": 1,
      "name": "reddit-comment-thread-scraper-with-score",
      "title": "Reddit Comment Thread Scraper with Score & Keyword Filters",
      "description": "Scrape Reddit comment threads, nested replies included, with author, score, depth, and edit/deletion flags, filtered by date, score, author, or keyword. Optional parent post record. No API key or login.",
      "version": "1.0",
      "buildTag": "latest",
      "dockerfile": "./Dockerfile",
      "readme": "# Reddit Comment Thread Scraper with Score & Keyword Filters\n\nExport Reddit comment threads — top-level comments and nested replies, including branches hidden behind \"load more replies\" and \"continue this thread\" — as flat, structured rows. Paste post links (or comment links), choose a sort, and get author, text, score, timestamps, reply depth, parent relationships, and moderation/deletion flags. No Reddit account, API key, or login required.\n\n## What this actor does\n\n- **Full thread reconstruction** — follows Reddit's own nested reply loaders and continue-thread pages until `maxComments` is reached or Reddit stops serving comments to logged-out readers\n- **6 comment sorts** — Best, Top, New, Controversial, Old, and Q&A, matching Reddit's UI\n- **Comment permalink focus** — paste a comment permalink with `focusOnTargetComment` enabled to get that comment plus its ancestor chain, including deeply nested targets\n- **Rich client-side filtering** — date window, reply-depth range, score range, author allow/deny, keyword include/exclude, length bounds, award count, OP-only, deleted/removed, collapsed\n- **Optional parent post record** — title, author, score, ratio, timestamp, subreddit, and text/link metadata when `includePost` is on\n- **Flexible URL input** — post URLs, comment permalinks, share links, `redd.it` short links, bare post IDs\n- **Omit-empty output** — a field appears only when Reddit actually exposes a value for it\n\n## Output per comment\n\n- `dataType` — `\"comment\"`\n- `comment_id`, `comment_name` — bare id and Reddit fullname (`t1_...`)\n- `post_id`, `post_url`, `post_title`, `link_id` — parent thread context\n- `permalink` — direct comment link\n- `author` — username (`[deleted]` when the account is gone)\n- `avatarUrl` — author avatar image, when available\n- `body` — comment text as readable plain text (block breaks preserved)\n- `body_html` — comment HTML from Reddit's renderer\n- `score` — net score (upvotes minus downvotes) at crawl time\n- `created_utc`, `created_at` — posted time (epoch and ISO-8601 UTC)\n- `edited`, `edited_at` — whether and when the comment was edited\n- `depth` — nesting level, `0` = top-level reply to the post\n- `position` — the comment's position among its siblings\n- `parentPositions` — the position path from the thread root. The prefix can be incomplete for branches reached through a \"continue this thread\" page; use `parent_id` for exact parentage\n- `parent_id`, `parent_kind` — direct parent fullname (`t3_...` for top-level or `t1_...` for a reply) and kind\n- `is_op` — author is the post's original poster\n- `collapsed` — Reddit collapses the comment by default\n- `isCommentDeleted`, `isAuthorDeleted` — comment content / author account was deleted\n- `contentType` — `text`, `image`, or `giphy`\n- `isHighlighted`, `hasPaidAward` — Reddit highlight/paid-award markers when present\n- `total_awards_received` — total award count (0 when none)\n- `subreddit`, `subreddit_prefixed`, `subreddit_id` — community context\n- `crawled_at` — when this record was fetched (ISO-8601)\n- `source` — `\"html\"`\n\nA rare deleted-comment placeholder that Reddit no longer gives an id for has no `comment_id`; its `isCommentDeleted`/`isAuthorDeleted` flags and position path still identify it. Its `parent_id` falls back to the post, so use the position path for its place in the tree.\n\nFields Reddit does not expose to logged-out readers are omitted: comment `author_id`, author flair fields, `ups`/`downs`, `score_hidden`, `controversiality`, `distinguished`, comment-level `stickied`/`locked`/`archived`, `collapsed_reason*`, legacy `gilded`, the award breakdown, and `removed_by_category`. See **Limitations** below.\n\n## Output per post\n\nEmitted once per post as the first record when `includePost` is on. Comment filters do not apply to it.\n\n- `dataType` — `\"post\"`\n- `post_id`, `post_name`, `title`, `author`, `author_id`\n- `subreddit`, `subreddit_prefixed`, `subreddit_id`\n- `url`, `permalink` — canonical reddit.com link\n- `content_url` — external destination for link posts\n- `domain`, `post_type`, `is_self`\n- `content`, `content_html` — self-post body\n- `score`, `upvote_ratio`, `num_comments`\n- `created_utc`, `created_at`\n- `language`\n- `is_nsfw`, `is_stickied`, `is_locked`\n- `total_awards_received`\n- `crawled_at`, `source`\n\nPost fields that require Reddit's legacy API data (flair, media/gallery URLs, poll data, crosspost counts, removal category, ups/downs, edited timestamp) are not available to logged-out readers and are omitted.\n\n## Input\n\n| Field | Type | Default | Description |\n|---|---|---|---|\n| `postUrls` | array | – (required) | Post URLs, comment URLs, share links, `redd.it` short links, or bare post IDs |\n| `maxComments` | integer | `100` | Maximum comments to keep per post, after filters (1–100,000) |\n| `commentSort` | select | `confidence` | `confidence` (Best), `top`, `new`, `controversial`, `old`, `qa` |\n| `includePost` | boolean | `false` | Emit the parent post as the first record of each thread |\n| `postedAfter` | date | – | Keep comments created on/after this date (UTC) |\n| `postedBefore` | date | – | Keep comments created on/before this date (UTC; a bare date means end of day) |\n| `minDepth` | integer | – | Keep comments at or deeper than this nesting level (0–100) |\n| `maxDepth` | integer | – | Keep comments at or above this nesting level (0–100) |\n| `minCommentScore` | integer | – | Keep comments with score ≥ this value |\n| `maxCommentScore` | integer | – | Keep comments with score ≤ this value |\n| `onlyOP` | boolean | `false` | Keep only the original poster's comments |\n| `excludeDeletedRemoved` | boolean | `false` | Drop comments Reddit marks deleted or author-deleted |\n| `excludeCollapsed` | boolean | `false` | Drop comments Reddit collapses by default |\n| `authors` | array | – | Keep only these usernames (case-insensitive) |\n| `excludeAuthors` | array | – | Drop these usernames (case-insensitive), e.g. bots |\n| `keywords` | array | – | Keep comments containing at least one keyword/phrase |\n| `excludeKeywords` | array | – | Drop comments containing any keyword/phrase |\n| `minAwards` | integer | – | Keep comments with at least this many total awards |\n| `minCommentLength` | integer | – | Minimum body length in characters (0–10,000) |\n| `maxCommentLength` | integer | – | Maximum body length in characters (0–10,000) |\n| `focusOnTargetComment` | boolean | `false` | For a comment permalink, return the target plus its ancestor chain. Works for deeply nested targets; the target must still be publicly visible |\n| `commentContext` | integer | `3` | Ancestor levels above the target comment when Reddit returns the focus view (0–8) |\n\n**Accepted `postUrls` forms:**\n- Full post URL: `https://www.reddit.com/r/programming/comments/1abc123/some_title/`\n- Short URL: `https://www.reddit.com/comments/1abc123` (also with `?sort=top`)\n- Comment URL: `https://www.reddit.com/r/x/comments/1abc123/title/comment/xyz987/`\n- Share link: `https://www.reddit.com/r/x/s/<token>`\n- Short link: `https://redd.it/1abc123`\n- Bare post ID: `1abc123` or `t3_1abc123`\n- `old.reddit.com` URLs also work\n\nMalformed items (empty strings, garbage URLs, non-strings) are skipped without failing the run.\n\n**Removed inputs:** `distinguishedFilter`, `controversialOnly`, and `excludeStickied` were removed because Reddit no longer exposes those markers to logged-out readers. Old saved tasks that still send them keep running; the actor logs one warning and ignores them.\n\n### Example: scrape a whole thread sorted by Top\n\n```json\n{\n  \"postUrls\": [\"https://www.reddit.com/r/technology/comments/1abc123/some_title/\"],\n  \"maxComments\": 500,\n  \"commentSort\": \"top\",\n  \"includePost\": true\n}\n```\n\n### Example: recent, high-signal comments only\n\n```json\n{\n  \"postUrls\": [\"https://www.reddit.com/r/investing/comments/1abc123/some_title/\"],\n  \"maxComments\": 1000,\n  \"postedAfter\": \"2026-06-01\",\n  \"minCommentScore\": 5,\n  \"excludeDeletedRemoved\": true\n}\n```\n\n### Example: NLP dataset — clean comment bodies\n\n```json\n{\n  \"postUrls\": [\n    \"https://www.reddit.com/r/AskReddit/comments/1abc123/some_title/\",\n    \"https://www.reddit.com/r/AskReddit/comments/1def456/other_title/\"\n  ],\n  \"maxComments\": 5000,\n  \"excludeDeletedRemoved\": true,\n  \"excludeAuthors\": [\"AutoModerator\"],\n  \"minCommentLength\": 20,\n  \"excludeKeywords\": [\"I am a bot\"]\n}\n```\n\n### Example: jump straight to one comment\n\n```json\n{\n  \"postUrls\": [\"https://www.reddit.com/r/x/comments/1abc123/title/comment/xyz987/\"],\n  \"focusOnTargetComment\": true,\n  \"commentContext\": 5\n}\n```\n\n## Use cases\n\n- **Sentiment & NLP datasets** — build large, structured corpora from real discussions\n- **Market & product research** — read what customers say in the replies, not just the top comment\n- **Community & moderation analysis** — study conversation depth, controversy, and OP engagement\n- **Launch monitoring** — track reaction to announcements across subreddits on a schedule\n- **Academic & social research** — export discussion trees for network and discourse analysis\n- **Support mining** — surface recurring complaints buried deep in a thread\n\n## FAQ\n\n**Do I need a Reddit account, API key, or login?**\nNo. The actor reads Reddit's public pages anonymously and stores no credentials.\n\n**Why are some fields missing compared with older versions?**\nReddit stopped serving its anonymous JSON endpoints in 2026 and does not render author IDs, vote splits, flair, moderator badges, or award details to logged-out readers. The actor emits every field the public pages still expose and omits the rest instead of inventing values.\n\n**Does it really capture the whole thread?**\nUp to `maxComments`, yes. The actor follows Reddit's nested reply loaders and continue-thread pages until the thread is exhausted or your limit is reached. Reddit's displayed comment count includes deleted and removed comments, so a complete export can be smaller than that number.\n\n**What is the difference between `parent_id` and `link_id`?**\n`link_id` always points to the parent post (`t3_...`). `parent_id` points to the comment's direct parent — the post for a top-level comment or another comment for a reply. `parent_kind` tells you which, and `parentPositions` gives the position path (with the continue-page caveat above).\n\n**How do I scrape just one comment instead of the whole thread?**\nPaste its permalink into `postUrls` and enable `focusOnTargetComment`. You get that comment plus up to `commentContext` levels of parent comments. The target must still be publicly visible; deleted/removed targets return no records.\n\n**Why does a deleted comment still appear?**\nDeleted comments are part of the thread structure. They carry `isCommentDeleted` / `isAuthorDeleted` and usually no body; enable `excludeDeletedRemoved` to drop them.\n\n**What happens for a private subreddit, banned post, or deleted post?**\nThe actor returns 0 records for that URL and continues with the other URLs. A status message explains when nothing could be read.\n\n**Do I need to configure a proxy?**\nNo. The actor selects the network route it needs automatically.\n\n**Is this affiliated with Reddit?**\nNo. This is an independent third-party actor that reads public web pages; it is not endorsed by or affiliated with Reddit, Inc.\n\n## Cost\n\nMeasured all-in run cost is roughly **$0.72–1.58 per 1,000 comments**, depending on thread depth, how many continue-thread branches are loaded, and how many deleted comments Reddit still counts. Small or heavily filtered runs have a higher fixed cost per 1,000. Store pricing is set separately.\n\n## Limitations\n\n- Reddit's own displayed comment count includes deleted, removed, and filtered comments that are not served to logged-out readers. The actor reports what is actually served.\n- Comment author IDs, vote up/down splits, flair, moderator/admin badges, controversy flags, award names/icons, and removal categories are not exposed anonymously. They are omitted rather than guessed. (The post record still carries the post author's ID when Reddit renders it.)\n- Some comments arrive from Reddit's lazy-loading fragments with `body_html` but without a plain-text `body` (and occasionally without either) even though they are not deleted. Use `body_html` as a fallback; the omission rate measured in cloud runs was ~15–18%.\n- `parentPositions` can lose the continue-target's position for branches loaded through \"continue this thread\" pages, so paths for those descendants may not match the tree exactly. `parent_id` remains correct.\n- Deleted-comment placeholders that Reddit renders without an id may carry the post as `parent_id` even when nested; their position path is the only tree hint for those rows.\n- Very large threads take longer because every hidden branch is loaded through Reddit's own pagination; there is no branch pruning, so cost scales with the served tree as well as `maxComments`.\n- The parent post record is intentionally thin: media, gallery, poll, crosspost, and flair details are not available on the public post page.\n",
      "input": {
        "title": "Reddit Comment Scraper Input",
        "type": "object",
        "schemaVersion": 1,
        "properties": {
          "postUrls": {
            "title": "Reddit Post URLs",
            "type": "array",
            "description": "Post URLs, comment URLs, share links or bare post IDs. Accepted forms: full post URL (https://www.reddit.com/r/x/comments/1abc/title/), short URL (https://www.reddit.com/comments/1abc or with ?sort=top), comment URL (.../comments/1abc/title/comment/xyz/), mobile share link (https://www.reddit.com/r/x/s/token), bare post id (1abc123) or t3_1abc123.",
            "editor": "stringList",
            "example": [
              "https://www.reddit.com/r/programming/comments/1vqukkf/nothing_like_a_monday_morning_github_outage/",
              "https://www.reddit.com/comments/1tlh5aj"
            ],
            "prefill": [
              "https://www.reddit.com/r/programming/comments/1vqukkf/"
            ],
            "minItems": 1
          },
          "maxComments": {
            "title": "Maximum comments per post",
            "type": "integer",
            "description": "Maximum number of comments to scrape from each post (including nested replies).",
            "minimum": 1,
            "maximum": 100000,
            "default": 100,
            "prefill": 30,
            "example": 100
          },
          "commentSort": {
            "title": "Sort comments by",
            "type": "string",
            "description": "How to sort the comment threads.",
            "enum": [
              "confidence",
              "top",
              "new",
              "controversial",
              "old",
              "qa"
            ],
            "default": "confidence",
            "prefill": "confidence",
            "enumTitles": [
              "Best (Confidence)",
              "Top",
              "New",
              "Controversial",
              "Old",
              "Q&A"
            ]
          },
          "includePost": {
            "title": "Include the parent post record",
            "type": "boolean",
            "description": "When enabled, the parent post metadata is emitted as the first record of each thread.",
            "default": false,
            "prefill": false
          },
          "postedAfter": {
            "title": "Comments created on or after (YYYY-MM-DD)",
            "type": "string",
            "description": "Only keep comments created on or after this date (UTC).",
            "editor": "datepicker",
            "example": "2025-01-01"
          },
          "postedBefore": {
            "title": "Comments created on or before (YYYY-MM-DD)",
            "type": "string",
            "description": "Only keep comments created on or before this date (interpreted as the end of that day, UTC).",
            "editor": "datepicker",
            "example": "2025-12-31"
          },
          "minDepth": {
            "title": "Minimum reply depth",
            "type": "integer",
            "description": "Only keep comments nested at or deeper than this depth (0 = keep everything, 1 = drop top-level comments and keep only replies, 2 = keep only replies-to-replies and deeper, etc.). Leave empty for no limit.",
            "minimum": 0,
            "maximum": 100,
            "example": 1
          },
          "maxDepth": {
            "title": "Maximum reply depth",
            "type": "integer",
            "description": "Only keep comments nested at or above this depth (0 = top-level comments only, 1 = top-level + first-level replies, etc.). Leave empty for no limit.",
            "minimum": 0,
            "maximum": 100,
            "example": 2
          },
          "minCommentScore": {
            "title": "Minimum comment score",
            "type": "integer",
            "description": "Only keep comments with a score (upvotes minus downvotes) at or above this value. Leave empty for no limit.",
            "minimum": -1000000,
            "maximum": 1000000,
            "example": 1
          },
          "maxCommentScore": {
            "title": "Maximum comment score",
            "type": "integer",
            "description": "Only keep comments with a score (upvotes minus downvotes) at or below this value. Leave empty for no limit.",
            "minimum": -1000000,
            "maximum": 1000000,
            "example": 1000
          },
          "onlyOP": {
            "title": "Only original poster's comments",
            "type": "boolean",
            "description": "When enabled, only comments written by the post's original author are kept.",
            "default": false,
            "prefill": false
          },
          "excludeDeletedRemoved": {
            "title": "Exclude deleted/removed comments",
            "type": "boolean",
            "description": "When enabled, comments that Reddit marks as deleted or author-deleted are dropped (no real content left to scrape).",
            "default": false,
            "prefill": false
          },
          "excludeCollapsed": {
            "title": "Exclude collapsed comments",
            "type": "boolean",
            "description": "When enabled, comments Reddit collapses by default (low score or crowd control) are dropped.",
            "default": false,
            "prefill": false
          },
          "authors": {
            "title": "Filter by comment author(s)",
            "type": "array",
            "description": "Only keep comments written by these Reddit usernames (case-insensitive). Leave empty to keep comments from all authors.",
            "editor": "stringList",
            "example": [
              "spez"
            ]
          },
          "excludeAuthors": {
            "title": "Exclude comment author(s)",
            "type": "array",
            "description": "Drop comments written by these Reddit usernames (case-insensitive), e.g. bots like AutoModerator. Leave empty to keep comments from all authors. Applied after `authors` (an author listed in both is excluded).",
            "editor": "stringList",
            "example": [
              "AutoModerator"
            ]
          },
          "keywords": {
            "title": "Filter by keyword(s) in comment body",
            "type": "array",
            "description": "Only keep comments whose body contains at least one of these keywords or phrases (case-insensitive substring match). Leave empty to keep all comments.",
            "editor": "stringList",
            "example": [
              "thanks",
              "great point"
            ]
          },
          "excludeKeywords": {
            "title": "Exclude keyword(s) in comment body",
            "type": "array",
            "description": "Drop comments whose body contains any of these keywords or phrases (case-insensitive substring match), e.g. spam/boilerplate phrases. Applied after `keywords` (a keyword listed in both still excludes the comment). Leave empty to keep all comments.",
            "editor": "stringList",
            "example": [
              "I am a bot",
              "check out my"
            ]
          },
          "minAwards": {
            "title": "Minimum total awards received",
            "type": "integer",
            "description": "Only keep comments with at least this many total awards. Leave empty for no limit.",
            "minimum": 0,
            "maximum": 1000000,
            "example": 1
          },
          "minCommentLength": {
            "title": "Minimum comment length (characters)",
            "type": "integer",
            "description": "Only keep comments whose body text is at least this many characters long. Useful for filtering out one-word/emoji-only noise when building NLP datasets. Leave empty for no limit.",
            "minimum": 0,
            "maximum": 10000,
            "example": 20
          },
          "maxCommentLength": {
            "title": "Maximum comment length (characters)",
            "type": "integer",
            "description": "Only keep comments whose body text is at most this many characters long (Reddit's hard cap is 10,000 characters). Leave empty for no limit.",
            "minimum": 0,
            "maximum": 10000,
            "example": 2000
          },
          "focusOnTargetComment": {
            "title": "Focus on the specific comment in the URL",
            "type": "boolean",
            "description": "When a `postUrls` item is a specific comment permalink, return that comment plus its ancestor chain (see `commentContext`). Works for deeply nested targets; the target must still be publicly visible. Has no effect on plain post URLs.",
            "default": false,
            "prefill": false
          },
          "commentContext": {
            "title": "Ancestor context depth (mode = focusOnTargetComment)",
            "type": "integer",
            "description": "How many levels of parent comments above the target comment to include when `focusOnTargetComment` is enabled (matches Reddit's own comment-permalink page). Ignored otherwise.",
            "minimum": 0,
            "maximum": 8,
            "default": 3,
            "prefill": 3
          }
        },
        "required": [
          "postUrls"
        ]
      },
      "output": {
        "actorOutputSchemaVersion": 1,
        "title": "Reddit Comment Thread Scraper with Score & Keyword Filters Output",
        "description": "Reddit comment threads, nested replies included, with author, score, depth, tree path, edited/deleted flags, and parent relationships, plus an optional parent post record.",
        "properties": {
          "commentAndPostRecords": {
            "type": "string",
            "title": "Comment & post records",
            "description": "Dataset containing one record per comment, plus the parent post record when 'Include post' is on. Records are distinguished by the `dataType` field (`comment` or `post`).",
            "template": "{{links.apiDefaultDatasetUrl}}/items"
          }
        }
      },
      "storages": {
        "dataset": {
          "actorSpecification": 1,
          "views": {
            "overview": {
              "title": "Reddit Comments",
              "transformation": {
                "fields": [
                  "post_title",
                  "author",
                  "body",
                  "score",
                  "depth",
                  "is_op",
                  "permalink",
                  "created_at"
                ]
              },
              "display": {
                "component": "table",
                "properties": {}
              }
            }
          }
        }
      },
      "environmentVariables": {},
      "defaultRunOptions": {
        "build": "latest",
        "timeoutSecs": 3600,
        "memoryMbytes": 1024
      }
    },
    "buildNumber": "1.0.2",
    "imageDigest": "ca1f6b70eead7a3ed87f447cf67c30521da775ba517bc12e2164328e01765d4c",
    "actVersion": {
      "sourceType": "SOURCE_FILES",
      "buildTag": "latest",
      "versionNumber": "1.0"
    },
    "usage": {
      "ACTOR_COMPUTE_UNITS": 0.06930222222222222
    }
  }
}