Reddit Subreddit Post Filter & Moderation Scraper
Scrape public subreddit listings and filter posts by keyword, score, comments, upvote ratio, age, flair, type, author, domain, NSFW or stickied status. Returns media, gallery, video, poll and crosspost fields. No login or API key.
Input schema
Input fields, types, required flags, descriptions, defaults, and prefills come from the published definition. Prefill examples are distinct from defaults.
| Field | Type | Required | Description |
|---|---|---|---|
subreddits | array | required | Public subreddits to scrape. Accepts names, r/ names or full Reddit URLs. |
sort | string | optional | How to order posts in each subreddit. Top and controversial use the time filter. |
timeFilter | string | optional | Time window used when Sort is Top or Controversial. Ignored for other sorts. |
Show all input fields
| Field | Type | Required | Description |
|---|---|---|---|
subreddits | array | required | Public subreddits to scrape. Accepts names, r/ names or full Reddit URLs. |
sort | string | optional | How to order posts in each subreddit. Top and controversial use the time filter. |
timeFilter | string | optional | Time window used when Sort is Top or Controversial. Ignored for other sorts. |
maxPosts | integer | optional | Stop after this many posts pass your filters. The actor keeps paging until it finds them or reaches Reddit's listing limit. |
maxItems | integer | optional | Overall cap across all subreddits. |
includeKeywords | array | optional | Keep posts containing at least one of these words or phrases. Matching is case-insensitive. |
excludeKeywords | array | optional | Drop posts containing any of these words or phrases. |
keywordMatch | string | optional | Any keyword, all keywords, or the exact phrase formed by joining the include keywords. |
searchIn | string | optional | Search the title, the body, or both. |
minScore | integer | optional | Drop posts with a lower score. Leave empty for no minimum. |
maxScore | integer | optional | Drop posts with a higher score. Leave empty for no maximum. |
minComments | integer | optional | Drop posts with fewer comments. Leave empty for no minimum. |
maxComments | integer | optional | Drop posts with more comments. Leave empty for no maximum. |
minUpvoteRatio | number | optional | Keep posts with at least this upvote ratio, from 0 to 1. Leave empty for no minimum. |
postedAfter | string | optional | ISO date (2026-10-01) or a relative value such as 7 days or 24 hours. Leave empty for no lower bound. |
postedBefore | string | optional | ISO date or a relative value such as 7 days. Leave empty for no upper bound. |
maxAgeDays | integer | optional | Shortcut for keeping only recent posts. Posted after can also be used. |
flairAllowlist | array | optional | Case-insensitive exact flair names. Leave empty for any flair. |
postType | string | optional | Keep one post type or all types. |
excludeStickied | boolean | optional | Drop pinned moderator/announcement posts. |
excludeNsfw | boolean | optional | Drop posts marked NSFW. NSFW-only communities are hidden anonymously, so this is a per-post filter. |
authorAllowlist | array | optional | Case-insensitive usernames. Leave empty for any author. |
authorBlocklist | array | optional | Case-insensitive usernames to drop. |
domainAllowlist | array | optional | Keep posts linking to these domains. Self-posts always pass. Accepts bare domains or URLs. |
domainBlocklist | array | optional | Drop posts linking to these domains. Self-posts always pass. |
includeSubredditInfo | boolean | optional | Emit one extra record per subreddit with its description, weekly active users and weekly contributions. Adds one request per subreddit. |
Download the published input schema
View complete JSON
{
"description": "Scrape public subreddit listings and keep only the posts you want. Filter by keywords, score, comments, upvote ratio, age, flair, post type, author, domain, NSFW and stickied status. No login, API key or proxy setup.",
"properties": {
"authorAllowlist": {
"default": [],
"description": "Case-insensitive usernames. Leave empty for any author.",
"editor": "stringList",
"prefill": [],
"sectionCaption": "Authors & domains",
"sectionDescription": "Optional allow and block lists. Self-posts always pass domain filters.",
"title": "Only these authors",
"type": "array"
},
"authorBlocklist": {
"default": [],
"description": "Case-insensitive usernames to drop.",
"editor": "stringList",
"prefill": [],
"title": "Block these authors",
"type": "array"
},
"domainAllowlist": {
"default": [],
"description": "Keep posts linking to these domains. Self-posts always pass. Accepts bare domains or URLs.",
"editor": "stringList",
"example": [
"github.com",
"arxiv.org"
],
"prefill": [],
"title": "Only these link domains",
"type": "array"
},
"domainBlocklist": {
"default": [],
"description": "Drop posts linking to these domains. Self-posts always pass.",
"editor": "stringList",
"prefill": [],
"title": "Block these link domains",
"type": "array"
},
"excludeKeywords": {
"default": [],
"description": "Drop posts containing any of these words or phrases.",
"editor": "stringList",
"prefill": [],
"title": "Exclude keywords",
"type": "array"
},
"excludeNsfw": {
"default": false,
"description": "Drop posts marked NSFW. NSFW-only communities are hidden anonymously, so this is a per-post filter.",
"prefill": false,
"title": "Exclude NSFW posts",
"type": "boolean"
},
"excludeStickied": {
"default": false,
"description": "Drop pinned moderator/announcement posts.",
"prefill": false,
"title": "Exclude stickied posts",
"type": "boolean"
},
"flairAllowlist": {
"default": [],
"description": "Case-insensitive exact flair names. Leave empty for any flair.",
"editor": "stringList",
"example": [
"Discussion",
"News"
],
"prefill": [],
"title": "Only these post flairs",
"type": "array"
},
"includeKeywords": {
"default": [],
"description": "Keep posts containing at least one of these words or phrases. Matching is case-insensitive.",
"editor": "stringList",
"example": [
"release",
"python"
],
"prefill": [],
"sectionCaption": "Post filters",
"sectionDescription": "Filters are applied before Max matching posts per subreddit is counted.",
"title": "Include keywords",
"type": "array"
},
"includeSubredditInfo": {
"default": false,
"description": "Emit one extra record per subreddit with its description, weekly active users and weekly contributions. Adds one request per subreddit.",
"prefill": false,
"sectionCaption": "Advanced",
"sectionDescription": "Optional extra output. The post scrape works without it.",
"title": "Include subreddit info records",
"type": "boolean"
},
"keywordMatch": {
"default": "any",
"description": "Any keyword, all keywords, or the exact phrase formed by joining the include keywords.",
"editor": "select",
"enum": [
"any",
"all",
"exact"
],
"enumTitles": [
"Any keyword",
"All keywords",
"Exact phrase"
],
"prefill": "any",
"title": "Keyword matching",
"type": "string"
},
"maxAgeDays": {
"description": "Shortcut for keeping only recent posts. Posted after can also be used.",
"maximum": 36500,
"minimum": 1,
"title": "Maximum post age in days",
"type": "integer"
},
"maxComments": {
"description": "Drop posts with more comments. Leave empty for no maximum.",
"maximum": 10000000,
"minimum": 0,
"title": "Maximum comments",
"type": "integer"
},
"maxItems": {
"default": 1000,
"description": "Overall cap across all subreddits.",
"maximum": 10000,
"minimum": 1,
"prefill": 10,
"title": "Max posts in total",
"type": "integer"
},
"maxPosts": {
"default": 25,
"description": "Stop after this many posts pass your filters. The actor keeps paging until it finds them or reaches Reddit's listing limit.",
"maximum": 1000,
"minimum": 1,
"prefill": 3,
"title": "Max matching posts per subreddit",
"type": "integer"
},
"maxScore": {
"description": "Drop posts with a higher score. Leave empty for no maximum.",
"maximum": 10000000,
"minimum": -10000,
"title": "Maximum score",
"type": "integer"
},
"minComments": {
"description": "Drop posts with fewer comments. Leave empty for no minimum.",
"maximum": 10000000,
"minimum": 0,
"title": "Minimum comments",
"type": "integer"
},
"minScore": {
"description": "Drop posts with a lower score. Leave empty for no minimum.",
"maximum": 10000000,
"minimum": -10000,
"title": "Minimum score",
"type": "integer"
},
"minUpvoteRatio": {
"description": "Keep posts with at least this upvote ratio, from 0 to 1. Leave empty for no minimum.",
"maximum": 1,
"minimum": 0,
"title": "Minimum upvote ratio",
"type": "number"
},
"postType": {
"default": "all",
"description": "Keep one post type or all types.",
"editor": "select",
"enum": [
"all",
"text",
"link",
"image",
"video",
"gallery",
"poll",
"crosspost",
"gif",
"multi_media"
],
"enumTitles": [
"All types",
"Text",
"Link",
"Image",
"Video",
"Gallery",
"Poll",
"Crosspost",
"GIF",
"Multi-media"
],
"prefill": "all",
"title": "Post type",
"type": "string"
},
"postedAfter": {
"description": "ISO date (2026-10-01) or a relative value such as 7 days or 24 hours. Leave empty for no lower bound.",
"editor": "textfield",
"title": "Posts created on or after",
"type": "string"
},
"postedBefore": {
"description": "ISO date or a relative value such as 7 days. Leave empty for no upper bound.",
"editor": "textfield",
"title": "Posts created on or before",
"type": "string"
},
"searchIn": {
"default": "title-and-body",
"description": "Search the title, the body, or both.",
"editor": "select",
"enum": [
"title-and-body",
"title",
"body"
],
"enumTitles": [
"Title and body",
"Title only",
"Body only"
],
"prefill": "title-and-body",
"title": "Search keywords in",
"type": "string"
},
"sort": {
"default": "hot",
"description": "How to order posts in each subreddit. Top and controversial use the time filter.",
"editor": "select",
"enum": [
"hot",
"new",
"top",
"rising",
"controversial",
"best"
],
"enumTitles": [
"Hot",
"New",
"Top",
"Rising",
"Controversial",
"Best"
],
"prefill": "new",
"title": "Sort posts by",
"type": "string"
},
"subreddits": {
"description": "Public subreddits to scrape. Accepts names, r/ names or full Reddit URLs.",
"editor": "stringList",
"example": [
"python",
"programming"
],
"prefill": [
"python"
],
"sectionCaption": "Subreddits & sorting",
"sectionDescription": "Pick the communities and how their posts should be ordered.",
"title": "Subreddits",
"type": "array"
},
"timeFilter": {
"default": "day",
"description": "Time window used when Sort is Top or Controversial. Ignored for other sorts.",
"editor": "select",
"enum": [
"hour",
"day",
"week",
"month",
"year",
"all"
],
"enumTitles": [
"Past hour",
"Past day",
"Past week",
"Past month",
"Past year",
"All time"
],
"prefill": "day",
"title": "Time range for top / controversial",
"type": "string"
}
},
"required": [
"subreddits"
],
"schemaVersion": 1,
"title": "Reddit Subreddit Post Filter & Moderation Scraper Input",
"type": "object"
}
Output schema
Published result links and storage locations. This schema is not an exhaustive contract for every field in a dataset record.
| Field | Type | Required | Description |
|---|---|---|---|
recordType | string | required | post, error, status or subreddit. |
type | string | optional | Set on error and status records. |
reason | string | optional | Why an error or status record was emitted, for example blocked, private, empty, no_matches or info_unavailable. |
message | string | optional | Human-readable explanation for an error or status record. |
page_title | string | optional | Title of the Reddit page that could not be read. |
httpStatus | string | optional | HTTP status from the failed request. |
postCount | string | optional | Number of posts emitted for a status record. |
filteredCount | string | optional | Number of fetched posts removed by filters. |
scrapedAt | string | optional | ISO 8601 timestamp for an error or status record. |
post_id | string | optional | Reddit post ID without the t3_ prefix. |
post_name | string | optional | Full Reddit post name, for example t3_abc123. |
title | string | optional | Post title. |
author | string | optional | Post author username. |
author_fullname | string | optional | Author account fullname, for example t2_abc123. |
author_id | string | optional | Author account ID. |
subreddit | string | optional | Subreddit name without r/. |
subreddit_prefixed | string | optional | Subreddit name with the r/ prefix. |
subreddit_id | string | optional | Subreddit fullname, for example t5_abc123. |
display_name | string | optional | Subreddit display name on an optional subreddit record. |
description | string | optional | Subreddit description on an optional subreddit record. |
weekly_active_users | string | optional | Reddit's weekly active user count on an optional subreddit record. |
weekly_contributions | string | optional | Reddit's weekly contribution count on an optional subreddit record. |
subscribers | string | optional | Subreddit subscriber count when the metadata source provides it. |
content | string | optional | Text body of a self post, first 1000 characters. Omitted for posts without a body. |
content_length | string | optional | Full character length of the text body. |
score | string | optional | Post score. |
num_comments | string | optional | Reported comment count. |
upvote_ratio | string | optional | Upvote ratio from 0 to 1. |
url | string | optional | Reddit permalink of the post. |
permalink | string | optional | Reddit permalink of the post. |
old_reddit_url | string | optional | Old Reddit permalink. Old Reddit may require a login. |
external_url | string | optional | Off-Reddit link, image, gallery or video URL when distinct from the permalink. |
domain | string | optional | Link domain, self.<subreddit> for text posts, or reddit.com for galleries. |
post_type | string | optional | text, link, image, video, gallery, poll, crosspost, gif or multi_media. |
post_hint | string | optional | Normalized post classification: self, image, gallery, hosted:video, rich:video or link. |
link_flair | string | optional | Post flair text when the post has one. |
link_flair_background_color | string | optional | Flair background color when Reddit provides one. |
thumbnail_url | string | optional | Post thumbnail or external preview image. |
preview_url | string | optional | Reddit preview image. |
image_url | string | optional | Full image URL for image and GIF posts. |
image_urls | string | optional | Image URLs belonging to the post, including gallery renditions. |
gallery_id | string | optional | Gallery identifier for gallery posts. |
gallery_images | string | optional | Full-size gallery image URLs. |
gallery_count | string | optional | Number of gallery images captured. |
is_self | string | optional | True for text and poll posts. |
is_stickied | string | optional | True when the post is pinned. |
is_nsfw | string | optional | True when the post is marked NSFW. |
is_spoiler | string | optional | True when the post is marked as a spoiler. |
is_promoted | string | optional | Not exposed by anonymous Reddit listings; omitted. |
is_original_content | string | optional | Not exposed by anonymous Reddit listings; omitted. |
is_gallery | string | optional | True for gallery posts. |
is_crosspost | string | optional | True for crossposts. |
gilded_count | string | optional | Not exposed as a gilded breakdown by anonymous Reddit listings; omitted. |
num_crossposts | string | optional | Not exposed by anonymous Reddit listings; omitted. |
crosspost_parent | string | optional | Parent post of a crosspost. |
crosspost_parent_id | string | optional | Parent post ID for a crosspost. |
crosspost_parent_subreddit | string | optional | Parent subreddit for a crosspost. |
crosspost_parent_url | string | optional | Parent post URL for a crosspost. |
total_awards_received | string | optional | Award count reported by Reddit. |
top_award_id | string | optional | ID of the top award when Reddit provides one. |
top_award_icon_url | string | optional | Icon URL of the top award when Reddit provides one. |
has_media | string | optional | True when the post has an image, gallery, video, embed or poll. |
media_type | string | optional | image, gallery, video, gif, poll or embed. |
video_url | string | optional | Stable direct video URL when available. |
video_hls_url | string | optional | HLS playlist URL. Signed URLs may expire. |
video_mp4_url | string | optional | MP4 rendition URL. Signed URLs may expire. |
video_poster_url | string | optional | Video poster image URL. |
video_duration_seconds | string | optional | Video duration in seconds. |
video_width | string | optional | Video width in pixels. |
video_height | string | optional | Video height in pixels. |
video_bitrate_kbps | string | optional | Video bitrate in kilobits per second. |
embed_provider | string | optional | Provider name for an external embed, for example YouTube. |
embed_html | string | optional | Embed iframe HTML when Reddit provides it. |
embed_title | string | optional | Title from the embed iframe. |
poll_data | string | optional | Poll state. Vote counts and option positions are captured; option labels are not always rendered anonymously. |
language | string | optional | Post language code reported by Reddit. |
title_word_count | string | optional | Number of words in the title. |
post_age_seconds | string | optional | Age of the post in seconds at scrape time. |
created_utc | string | optional | Unix timestamp of post creation. |
created_at | string | optional | ISO 8601 creation time. |
crawled_at | string | optional | ISO 8601 timestamp of this scrape. |
Download the published output schema
View complete JSON
{
"actorOutputSchemaVersion": 1,
"description": "One record per post that passes your filters. Error and status records explain blocked, private, empty or no-match subreddits. Optional subreddit records add community metadata.",
"properties": {
"author": {
"description": "Post author username.",
"template": "{{author}}",
"title": "Author",
"type": "string"
},
"author_fullname": {
"description": "Author account fullname, for example t2_abc123.",
"template": "{{author_fullname}}",
"title": "Author fullname",
"type": "string"
},
"author_id": {
"description": "Author account ID.",
"template": "{{author_id}}",
"title": "Author ID",
"type": "string"
},
"content": {
"description": "Text body of a self post, first 1000 characters. Omitted for posts without a body.",
"template": "{{content}}",
"title": "Content",
"type": "string"
},
"content_length": {
"description": "Full character length of the text body.",
"template": "{{content_length}}",
"title": "Content length",
"type": "string"
},
"crawled_at": {
"description": "ISO 8601 timestamp of this scrape.",
"template": "{{crawled_at}}",
"title": "Crawled at",
"type": "string"
},
"created_at": {
"description": "ISO 8601 creation time.",
"template": "{{created_at}}",
"title": "Created at",
"type": "string"
},
"created_utc": {
"description": "Unix timestamp of post creation.",
"template": "{{created_utc}}",
"title": "Created UTC",
"type": "string"
},
"crosspost_parent": {
"description": "Parent post of a crosspost.",
"template": "{{crosspost_parent}}",
"title": "Crosspost parent",
"type": "string"
},
"crosspost_parent_id": {
"description": "Parent post ID for a crosspost.",
"template": "{{crosspost_parent_id}}",
"title": "Crosspost parent ID",
"type": "string"
},
"crosspost_parent_subreddit": {
"description": "Parent subreddit for a crosspost.",
"template": "{{crosspost_parent_subreddit}}",
"title": "Crosspost parent subreddit",
"type": "string"
},
"crosspost_parent_url": {
"description": "Parent post URL for a crosspost.",
"template": "{{crosspost_parent_url}}",
"title": "Crosspost parent URL",
"type": "string"
},
"description": {
"description": "Subreddit description on an optional subreddit record.",
"template": "{{description}}",
"title": "Description",
"type": "string"
},
"display_name": {
"description": "Subreddit display name on an optional subreddit record.",
"template": "{{display_name}}",
"title": "Display name",
"type": "string"
},
"domain": {
"description": "Link domain, self.<subreddit> for text posts, or reddit.com for galleries.",
"template": "{{domain}}",
"title": "Domain",
"type": "string"
},
"embed_html": {
"description": "Embed iframe HTML when Reddit provides it.",
"template": "{{embed_html}}",
"title": "Embed HTML",
"type": "string"
},
"embed_provider": {
"description": "Provider name for an external embed, for example YouTube.",
"template": "{{embed_provider}}",
"title": "Embed provider",
"type": "string"
},
"embed_title": {
"description": "Title from the embed iframe.",
"template": "{{embed_title}}",
"title": "Embed title",
"type": "string"
},
"external_url": {
"description": "Off-Reddit link, image, gallery or video URL when distinct from the permalink.",
"template": "{{external_url}}",
"title": "External URL",
"type": "string"
},
"filteredCount": {
"description": "Number of fetched posts removed by filters.",
"template": "{{filteredCount}}",
"title": "Filtered count",
"type": "string"
},
"gallery_count": {
"description": "Number of gallery images captured.",
"template": "{{gallery_count}}",
"title": "Gallery count",
"type": "string"
},
"gallery_id": {
"description": "Gallery identifier for gallery posts.",
"template": "{{gallery_id}}",
"title": "Gallery ID",
"type": "string"
},
"gallery_images": {
"description": "Full-size gallery image URLs.",
"template": "{{gallery_images}}",
"title": "Gallery images",
"type": "string"
},
"gilded_count": {
"description": "Not exposed as a gilded breakdown by anonymous Reddit listings; omitted.",
"template": "{{gilded_count}}",
"title": "Gilded count",
"type": "string"
},
"has_media": {
"description": "True when the post has an image, gallery, video, embed or poll.",
"template": "{{has_media}}",
"title": "Has media",
"type": "string"
},
"httpStatus": {
"description": "HTTP status from the failed request.",
"template": "{{httpStatus}}",
"title": "HTTP status",
"type": "string"
},
"image_url": {
"description": "Full image URL for image and GIF posts.",
"template": "{{image_url}}",
"title": "Image URL",
"type": "string"
},
"image_urls": {
"description": "Image URLs belonging to the post, including gallery renditions.",
"template": "{{image_urls}}",
"title": "Image URLs",
"type": "string"
},
"is_crosspost": {
"description": "True for crossposts.",
"template": "{{is_crosspost}}",
"title": "Is crosspost",
"type": "string"
},
"is_gallery": {
"description": "True for gallery posts.",
"template": "{{is_gallery}}",
"title": "Is gallery",
"type": "string"
},
"is_nsfw": {
"description": "True when the post is marked NSFW.",
"template": "{{is_nsfw}}",
"title": "Is NSFW",
"type": "string"
},
"is_original_content": {
"description": "Not exposed by anonymous Reddit listings; omitted.",
"template": "{{is_original_content}}",
"title": "Is original content",
"type": "string"
},
"is_promoted": {
"description": "Not exposed by anonymous Reddit listings; omitted.",
"template": "{{is_promoted}}",
"title": "Is promoted",
"type": "string"
},
"is_self": {
"description": "True for text and poll posts.",
"template": "{{is_self}}",
"title": "Is self post",
"type": "string"
},
"is_spoiler": {
"description": "True when the post is marked as a spoiler.",
"template": "{{is_spoiler}}",
"title": "Is spoiler",
"type": "string"
},
"is_stickied": {
"description": "True when the post is pinned.",
"template": "{{is_stickied}}",
"title": "Is stickied",
"type": "string"
},
"language": {
"description": "Post language code reported by Reddit.",
"template": "{{language}}",
"title": "Language",
"type": "string"
},
"link_flair": {
"description": "Post flair text when the post has one.",
"template": "{{link_flair}}",
"title": "Link flair",
"type": "string"
},
"link_flair_background_color": {
"description": "Flair background color when Reddit provides one.",
"template": "{{link_flair_background_color}}",
"title": "Link flair background",
"type": "string"
},
"media_type": {
"description": "image, gallery, video, gif, poll or embed.",
"template": "{{media_type}}",
"title": "Media type",
"type": "string"
},
"message": {
"description": "Human-readable explanation for an error or status record.",
"template": "{{message}}",
"title": "Message",
"type": "string"
},
"num_comments": {
"description": "Reported comment count.",
"template": "{{num_comments}}",
"title": "Number of comments",
"type": "string"
},
"num_crossposts": {
"description": "Not exposed by anonymous Reddit listings; omitted.",
"template": "{{num_crossposts}}",
"title": "Number of crossposts",
"type": "string"
},
"old_reddit_url": {
"description": "Old Reddit permalink. Old Reddit may require a login.",
"template": "{{old_reddit_url}}",
"title": "Old Reddit URL",
"type": "string"
},
"page_title": {
"description": "Title of the Reddit page that could not be read.",
"template": "{{page_title}}",
"title": "Page title",
"type": "string"
},
"permalink": {
"description": "Reddit permalink of the post.",
"template": "{{permalink}}",
"title": "Permalink",
"type": "string"
},
"poll_data": {
"description": "Poll state. Vote counts and option positions are captured; option labels are not always rendered anonymously.",
"template": "{{poll_data}}",
"title": "Poll data",
"type": "string"
},
"postCount": {
"description": "Number of posts emitted for a status record.",
"template": "{{postCount}}",
"title": "Post count",
"type": "string"
},
"post_age_seconds": {
"description": "Age of the post in seconds at scrape time.",
"template": "{{post_age_seconds}}",
"title": "Post age",
"type": "string"
},
"post_hint": {
"description": "Normalized post classification: self, image, gallery, hosted:video, rich:video or link.",
"template": "{{post_hint}}",
"title": "Post hint",
"type": "string"
},
"post_id": {
"description": "Reddit post ID without the t3_ prefix.",
"template": "{{post_id}}",
"title": "Post ID",
"type": "string"
},
"post_name": {
"description": "Full Reddit post name, for example t3_abc123.",
"template": "{{post_name}}",
"title": "Post name",
"type": "string"
},
"post_type": {
"description": "text, link, image, video, gallery, poll, crosspost, gif or multi_media.",
"template": "{{post_type}}",
"title": "Post type",
"type": "string"
},
"preview_url": {
"description": "Reddit preview image.",
"template": "{{preview_url}}",
"title": "Preview URL",
"type": "string"
},
"reason": {
"description": "Why an error or status record was emitted, for example blocked, private, empty, no_matches or info_unavailable.",
"template": "{{reason}}",
"title": "Reason",
"type": "string"
},
"recordType": {
"description": "post, error, status or subreddit.",
"template": "{{recordType}}",
"title": "Record type",
"type": "string"
},
"score": {
"description": "Post score.",
"template": "{{score}}",
"title": "Score",
"type": "string"
},
"scrapedAt": {
"description": "ISO 8601 timestamp for an error or status record.",
"template": "{{scrapedAt}}",
"title": "Scraped at",
"type": "string"
},
"subreddit": {
"description": "Subreddit name without r/.",
"template": "{{subreddit}}",
"title": "Subreddit",
"type": "string"
},
"subreddit_id": {
"description": "Subreddit fullname, for example t5_abc123.",
"template": "{{subreddit_id}}",
"title": "Subreddit ID",
"type": "string"
},
"subreddit_prefixed": {
"description": "Subreddit name with the r/ prefix.",
"template": "{{subreddit_prefixed}}",
"title": "Subreddit prefixed",
"type": "string"
},
"subscribers": {
"description": "Subreddit subscriber count when the metadata source provides it.",
"template": "{{subscribers}}",
"title": "Subscribers",
"type": "string"
},
"thumbnail_url": {
"description": "Post thumbnail or external preview image.",
"template": "{{thumbnail_url}}",
"title": "Thumbnail URL",
"type": "string"
},
"title": {
"description": "Post title.",
"template": "{{title}}",
"title": "Title",
"type": "string"
},
"title_word_count": {
"description": "Number of words in the title.",
"template": "{{title_word_count}}",
"title": "Title word count",
"type": "string"
},
"top_award_icon_url": {
"description": "Icon URL of the top award when Reddit provides one.",
"template": "{{top_award_icon_url}}",
"title": "Top award icon URL",
"type": "string"
},
"top_award_id": {
"description": "ID of the top award when Reddit provides one.",
"template": "{{top_award_id}}",
"title": "Top award ID",
"type": "string"
},
"total_awards_received": {
"description": "Award count reported by Reddit.",
"template": "{{total_awards_received}}",
"title": "Total awards",
"type": "string"
},
"type": {
"description": "Set on error and status records.",
"template": "{{type}}",
"title": "Internal record type",
"type": "string"
},
"upvote_ratio": {
"description": "Upvote ratio from 0 to 1.",
"template": "{{upvote_ratio}}",
"title": "Upvote ratio",
"type": "string"
},
"url": {
"description": "Reddit permalink of the post.",
"template": "{{url}}",
"title": "URL",
"type": "string"
},
"video_bitrate_kbps": {
"description": "Video bitrate in kilobits per second.",
"template": "{{video_bitrate_kbps}}",
"title": "Video bitrate",
"type": "string"
},
"video_duration_seconds": {
"description": "Video duration in seconds.",
"template": "{{video_duration_seconds}}",
"title": "Video duration",
"type": "string"
},
"video_height": {
"description": "Video height in pixels.",
"template": "{{video_height}}",
"title": "Video height",
"type": "string"
},
"video_hls_url": {
"description": "HLS playlist URL. Signed URLs may expire.",
"template": "{{video_hls_url}}",
"title": "Video HLS URL",
"type": "string"
},
"video_mp4_url": {
"description": "MP4 rendition URL. Signed URLs may expire.",
"template": "{{video_mp4_url}}",
"title": "Video MP4 URL",
"type": "string"
},
"video_poster_url": {
"description": "Video poster image URL.",
"template": "{{video_poster_url}}",
"title": "Video poster URL",
"type": "string"
},
"video_url": {
"description": "Stable direct video URL when available.",
"template": "{{video_url}}",
"title": "Video URL",
"type": "string"
},
"video_width": {
"description": "Video width in pixels.",
"template": "{{video_width}}",
"title": "Video width",
"type": "string"
},
"weekly_active_users": {
"description": "Reddit's weekly active user count on an optional subreddit record.",
"template": "{{weekly_active_users}}",
"title": "Weekly active users",
"type": "string"
},
"weekly_contributions": {
"description": "Reddit's weekly contribution count on an optional subreddit record.",
"template": "{{weekly_contributions}}",
"title": "Weekly contributions",
"type": "string"
}
},
"required": [
"recordType"
],
"schemaVersion": 1,
"title": "Reddit Subreddit Post Filter & Moderation Scraper Output",
"type": "object"
}
Dataset schema
Published dataset views and any declared field definitions. A view can select only a subset of the returned record fields.
Download the published dataset schema
View complete JSON
{
"actorSpecification": 1,
"views": {
"overview": {
"display": {
"component": "table",
"properties": {}
},
"title": "Reddit Posts (Pro)",
"transformation": {
"fields": [
"recordType",
"subreddit_prefixed",
"title",
"author",
"score",
"upvote_ratio",
"num_comments",
"post_type",
"created_at",
"link_flair",
"url",
"display_name",
"description",
"subscribers"
]
}
}
}
}
Key-value schema
Published key-value store definitions, when provided by this build.
This definition is not present in the default published build. See the published README for available documentation.
Published README
Read the full published documentation
Reddit Subreddit Post Filter & Moderation Scraper
Pull posts from public subreddits, then keep only the ones that matter. This actor reads Reddit's public subreddit listings, applies a full set of filters before it counts toward your limit, and writes clean post records with score, comments, flair, media, gallery, video, poll and crosspost data.
No Reddit login, API key, cookie or proxy setup is required.
What it does
- Scrapes one or many public subreddits, sorted hot, new, top, rising, controversial or best.
- Applies the time filter for top and controversial.
- Keeps paging until it finds the requested number of matching posts or reaches Reddit's listing limit.
- Filters by keywords, score, comment count, upvote ratio, date/age, flair, post type, author, domain, NSFW and stickied status.
- Emits explicit
statusorerrorrecords when a subreddit is blocked, private, banned, missing or empty, instead of silently returning nothing. - Optionally emits one community metadata record per subreddit.
Input
| Field | Type | Default | Description |
|---|---|---|---|
subreddits |
array | — | Subreddit names, r/ names or full Reddit URLs. Required. |
sort |
select | hot |
hot, new, top, rising, controversial or best. |
timeFilter |
select | day |
hour, day, week, month, year or all; used with top/controversial. |
maxPosts |
integer | 25 |
Maximum matching posts per subreddit (1–1000). |
maxItems |
integer | 1000 |
Overall cap across all subreddits (1–10,000). |
includeKeywords |
array | [] |
Keep posts containing any/all of these words or phrases. |
excludeKeywords |
array | [] |
Drop posts containing any of these words or phrases. |
keywordMatch |
select | any |
any, all or exact (join include keywords into one phrase). |
searchIn |
select | title-and-body |
title-and-body, title or body. |
minScore / maxScore |
integer | empty | Score range. |
minComments / maxComments |
integer | empty | Comment-count range. |
minUpvoteRatio |
number | empty | Minimum upvote ratio, 0–1. |
postedAfter / postedBefore |
string | empty | ISO date or relative value such as 7 days or 24 hours. |
maxAgeDays |
integer | empty | Shortcut for a recent-post cutoff. |
flairAllowlist |
array | [] |
Only these exact flair names (case-insensitive). |
postType |
select | all |
all, text, link, image, video, gallery, poll, crosspost, gif or multi_media. |
excludeStickied |
boolean | false |
Drop pinned posts. |
excludeNsfw |
boolean | false |
Drop posts marked NSFW. |
authorAllowlist / authorBlocklist |
array | [] |
Username allow/block lists. |
domainAllowlist / domainBlocklist |
array | [] |
Link-domain allow/block lists. Self-posts pass. |
includeSubredditInfo |
boolean | false |
Emit one community metadata record per subreddit. |
Examples
High-score, clean posts:
{
"subreddits": ["python", "programming"],
"sort": "top",
"timeFilter": "week",
"minScore": 100,
"excludeNsfw": true,
"excludeStickied": true,
"maxPosts": 50
}
Keyword monitoring:
{
"subreddits": ["technology", "artificial"],
"sort": "new",
"includeKeywords": ["regulation", "policy"],
"excludeKeywords": ["giveaway"],
"postedAfter": "7 days",
"maxPosts": 100
}
Trusted-domain links only:
{
"subreddits": ["machinelearning"],
"sort": "hot",
"domainAllowlist": ["arxiv.org", "github.com"],
"maxPosts": 30
}
Output
Each post record includes:
recordType(post),post_id,post_name,titleauthor,author_fullname,author_idsubreddit,subreddit_prefixed,subreddit_idcontent,content_length(self-post body;contentis capped at 1,000 characters)score,num_comments,upvote_ratiourl,permalink,old_reddit_url,external_url,domainpost_type,post_hint,languagelink_flair,link_flair_background_colorthumbnail_url,preview_url,image_url,image_urlsgallery_id,gallery_images,gallery_countis_self,is_stickied,is_nsfw,is_spoiler,is_gallery,is_crosspostcrosspost_parent,crosspost_parent_id,crosspost_parent_subreddit,crosspost_parent_urltotal_awards_received,top_award_id,top_award_icon_urlhas_media,media_typevideo_url,video_hls_url,video_mp4_url,video_poster_url,video_duration_seconds,video_width,video_height,video_bitrate_kbpsembed_provider,embed_html,embed_titlepoll_data(closed state, total votes, option positions/votes where rendered)title_word_count,post_age_seconds,created_utc,created_at,crawled_at
Optional community record (recordType: "subreddit"): subreddit, subreddit_prefixed, subreddit_id, display_name, description, subscribers, created_utc, created_at, crawled_at. weekly_active_users and weekly_contributions are included only when the direct Reddit page header provides them; the Arctic Shift metadata fallback does not expose those two values, so they are omitted rather than fabricated.
Status/error records explain what happened without failing the run:
recordType: "error"withreason(blocked,login_required,private,banned,not_found,nsfw_gated,rate_limited,http_error),message,subreddit, optionalpage_titleandhttpStatus.recordType: "status"withreason(empty,no_matches,rising_cap,info_unavailable),message,postCount,filteredCount.
Fields Reddit does not expose anonymously are never fabricated. is_promoted, is_original_content, gilded_count and num_crossposts remain documented for compatibility but are omitted because the public listing does not carry reliable values. old_reddit_url may redirect to a login page on old Reddit.
Limits and behavior
- Reddit's full HTML listing feeds stop at roughly 1,000 posts for hot/new and roughly 500 for top/controversial. The cloud RSS fallback is capped at 100 posts per subreddit. Split by sort, time range or subreddit to collect more.
risingis a small live listing; it may return fewer posts thanmaxPosts.maxPostscounts matching posts, not scanned posts. If a filter is restrictive, the actor pages deeper until it finds matches or reaches the listing ceiling.- NSFW-only communities are age-gated for anonymous visitors and may appear as
not_foundornsfw_gated. Per-post NSFW filtering works when the listing carries the flag. - Poll option labels are not always rendered in the public listing; vote counts and positions are captured when present.
- Video HLS/MP4 URLs can be signed and expire.
video_urluses a stable low-resolution fallback; the HLS/MP4 fields are provided for convenience. - If Reddit blocks the direct listing, the actor first reads the same listing through a Google Translate mirror. That mirror keeps the full post fields. If it is unavailable, the actor falls back to the public Atom/RSS feed. The RSS fallback carries title, author, URL, body and timestamps, but not score, comment count, media, gallery, video or poll fields; the run status message says when it is used.
- If score, comment-count or upvote-ratio filters are set and only RSS is available, the actor does not silently pass those filters. It emits a clear
metrics_unavailableerror record for that subreddit and skips it, rather than returning unfiltered posts as if they matched. - When
includeSubredditInfois enabled, the actor tries the direct subreddit page header first and falls back to Arctic Shift's publicsubreddits/searchendpoint. Arctic supplies subscribers, description and creation time; weekly active users and weekly contributions are not available there, so those fields are omitted. - The actor paces requests and backs off on rate limits. It never uses a wall-clock stop that silently under-delivers rows.
FAQ
Do I need a Reddit account, API key or proxy? No. The actor reads public listing HTML directly and escalates its own internal proxy only if Reddit blocks the request.
Why did I get a status or error record instead of posts?
The subreddit may be private, banned, missing, age-gated, temporarily blocked or empty. The record's reason and message explain which case applies. The rest of the run continues.
Why did I get a metrics_unavailable error?
Reddit’s RSS fallback has no score, comment count or upvote ratio. The actor will not pretend a metric filter passed on RSS-only data. It skips that subreddit and emits the error; retry when the full listing or Translate mirror is available, or remove the metric filter.
Why are my filters returning fewer posts than maxPosts?
maxPosts is the number of posts that pass your filters. If the source runs out before that, the actor returns what exists. The run status message reports filtered outages.
Can I scrape comments? No. This actor is focused on subreddit posts and moderation filters. Use a dedicated Reddit comment scraper for threads.
Is this affiliated with Reddit? No. This is an independent, third-party actor that reads public Reddit pages. It is not affiliated with or endorsed by Reddit, Inc.
Python quickstart
Use a schema-derived input and the Python standard library to start a run. An Apify account and token are required for execution.
Source build BLjZQoaYOw4cv3DKH. Documentation is regenerated from the default published build; private drafts are not included.