Reddit Historical Archive Scraper | Enterprise Grade
Under maintenancePricing
$11.99/month + usage
Reddit Historical Archive Scraper | Enterprise Grade
Under maintenanceAll-in-one Reddit Scraper. Scrape posts and full comment threads from any search, subreddit, user, or direct post URL. This enterprise-grade scraper is the fastest in the market and delivers clean and detailed JSON.
Pricing
$11.99/month + usage
Rating
5.0
(1)
Developer
Fatih Tahta
Maintained by CommunityActor stats
5
Bookmarked
49
Total users
2
Monthly active users
35 days
Issues response
4 days ago
Last modified
Categories
Share
Historical Reddit Archive Search
Actor name: reddit-historical-archive-search
Overview
Historical Reddit Archive Search finds Reddit submissions in a stored, indexed archive instead of crawling Reddit live. Give it one or more search phrases, optionally focus on a subreddit or UTC date range, and receive structured post records ready for research, analytics, monitoring, enrichment, or ETL workflows.
The Actor is designed for repeatable historical research. Keyword searches use proprietary month-partitioned full-text indexes, while keyword-free subreddit listings use proprietary subreddit hash buckets derived from the same canonical archive. Matching records stream to the default dataset, duplicate Reddit IDs are removed across overlapping queries and archive pages, optional relevance and engagement filters are applied, and a machine-readable run summary is saved for review and automation.
This Actor searches archived posts only. It does not visit live Reddit pages, scrape direct Reddit URLs, collect comment threads, or guarantee that every historical Reddit submission exists in the underlying archive.
Table of Contents
- Archive at a Glance
- Why Use This Actor
- Feature Highlights
- Common Research Workflows
- Choosing a Search Strategy
- How Archive Search Works
- Input Parameters
- Example Inputs
- Understanding Input Interactions
- Output
- Field Reference
- Data Guarantees and Limitations
- Performance
- How to Run on Apify
- Scheduling and Automation
- Export and Integration Options
- Recommended Production Patterns
- Responsible Use
- Troubleshooting
- Frequently Asked Questions
- Support
Archive at a Glance
This is not a thin wrapper around Reddit's current search results. It is backed by a privately operated, month-partitioned archive built for historical discovery at scale.
- 29.2+ billion verified archive rows: The audited storage layers contain approximately 2.60 billion submission rows and 26.62 billion comment rows.
- More than 20 years of stored history: Verified archive material spans June 2005 through May 2026, with additional months and indexes added as they become available.
- Indexed instead of live-scrolled: Keyword runs use monthly full-text indexes, and complete subreddit listings fetch one compact owned hash bucket per month rather than downloading an entire month or stopping at the practical depth of a live feed.
- Search by topic or community: Find archived posts with one or more keyword phrases, optionally restricted to a subreddit and an exact UTC date range.
- Fast archive retrieval: A measured full-month index lookup over 47,453,180 submissions returned broad-query candidates in approximately 0.12 seconds after the index was available locally. End-to-end Actor time also includes startup, network transfer, filtering, enrichment, and dataset writes.
- Progressive results: Matching posts are written to the dataset as monthly windows finish, so large historical searches do not need to complete before results become visible.
- Analysis-ready output: Optional deterministic sentiment analysis adds polarity, confidence, and normalized scores. Optional content analysis adds conservative taxonomy labels and paths when the record contains enough evidence.
- Research controls built in: Apply strict token matching, subreddit targeting, score and comment-count thresholds, NSFW controls, deduplication, and global output limits without assembling a separate pipeline.
The underlying archive also stores comments, but the current public Actor searches and returns submissions only. Comment counts on post records are archived metadata, not collected comment threads. Storage totals describe verified rows across the base and repair/backfill layers; they are not a claim that every Reddit record is unique, complete, or currently exposed through this Actor.
Why Use This Actor
- Market and consumer research: Study historical opinions, category discussions, purchase considerations, and recurring user pain points.
- Brand and competitor monitoring: Find older mentions, launch reactions, reputation signals, and shifts in community discussion.
- Product research: Discover feature requests, complaints, workflows, and language customers used at different points in time.
- Trend analysis: Compare topics across communities and fixed reporting periods without depending on Reddit's current search window.
- Data engineering: Export stable, structured JSON records into warehouses, dashboards, search indexes, or enrichment pipelines.
- AI and NLP workflows: Add deterministic sentiment and content-category fields before routing records into downstream models or agents.
Feature Highlights
- Historical keyword discovery: Search stored Reddit submissions beyond the practical window exposed by live Reddit search.
- Multiple search phrases: Run several related phrases in one job and receive one overlap-safe dataset.
- Subreddit targeting: Restrict research to a specific community without relying on live subreddit pages.
- Exact UTC date ranges: Reproduce fixed reporting periods with inclusive
dateFromanddateToboundaries. - Rolling time windows: Use current-day, seven-day, 30-day, 365-day, or all-time archive windows.
- Progressive monthly collection: Search newest archive months first and save rows while the Actor continues through the selected range.
- Strict query verification: Require every query token to occur in a post's title, body, or canonical URL.
- Engagement thresholds: Keep only records meeting minimum score or discussion-size requirements.
- NSFW controls: Exclude archived 18+ records by default or include them explicitly.
- Deterministic sentiment: Add polarity, normalized score, confidence, and human-readable sentiment labels.
- Conservative content classification: Attach taxonomy labels only when the post contains enough evidence.
- Rich normalized records: Receive stable IDs, canonical URLs, temporal dimensions, engagement metrics, media details, and analysis-friendly derived fields.
- Run-level deduplication: Save each Reddit
kind:ididentity once even when several queries, pages, or months overlap. - Bounded filtered pagination: Continue through archive pages to fill a requested limit without launching an unrestricted backend scan.
- Machine-readable summaries: Inspect found, filtered, duplicate, saved, monthly, page, stop-reason, and broker metadata.
- ETL-friendly output: Export consistent rows to JSON, JSONL, CSV, Excel, XML, RSS, or supported Apify integrations.
Common Research Workflows
Brand history and reputation research
Search a brand name, product name, former brand name, and common misspellings across a fixed historical period. Add a subreddit filter when community context matters, or leave it empty to compare where conversations occurred organically.
Useful settings:
{"queries": ["ExampleBrand","Example Product","ExampleBrand alternative"],"dateFrom": "2018-01-01","dateTo": "2025-12-31","sentiment_analysis": true,"content_analysis": true,"maxPosts": 2000}
Product feedback discovery
Combine product and problem-oriented phrases such as battery issue, pricing problem, feature request, and switched from. Use minimumComments to focus on posts that produced a meaningful discussion.
Competitive intelligence
Run separate scheduled inputs for each competitor so datasets and summaries remain easy to compare. Use identical date windows and engagement thresholds across jobs for cleaner longitudinal analysis.
Historical trend analysis
Use fixed monthly, quarterly, or yearly windows and retain created_year, created_month, created_day_of_week, and subreddit fields in your downstream model. Fixed exact dates make reruns reproducible.
Community research
Set subredditName by itself to collect every indexed post from that community in the selected time window. Add subredditKeywords only when you want to narrow the subreddit listing to a topic. The Actor searches archived submissions; it does not crawl the current subreddit feed.
Dataset construction for NLP
Disable content classification when you plan to supply your own taxonomy. Keep sentiment enabled for a lightweight baseline, or disable both enrichment options when you only need source and normalized metadata.
Academic and policy research
Use exact date windows, preserve the input and RUN-SUMMARY, and document archive-coverage limitations alongside any conclusions. The dataset provides useful temporal and community features, but archive presence is not a complete census of Reddit.
Choosing a Search Strategy
Start broad, then narrow
A practical workflow is:
- run one phrase over one representative month with
maxPostsbetween 25 and 100; - inspect titles, bodies, subreddits,
recordsFound, andrecordsFiltered; - add a subreddit or strict matching only when irrelevant candidates are visible;
- add engagement thresholds only when low-activity posts are not useful;
- expand the date window after the query behavior is understood.
Use several focused phrases instead of one long sentence
Archive full-text search generally works best with short topic phrases, names, product terms, or recognizable wording. A long natural-language question may be unnecessarily restrictive.
For example, prefer:
{"queries": ["electric bike battery","ebike range","electric bike commute"]}
over one long question containing every concept.
Understand query overlap
The same Reddit post can match several input phrases. The Actor preserves the first normalized match it saves and removes later rows with the same kind:id identity. recordsDuplicate reports how many repeated identities were removed.
Choose precision or recall deliberately
- Leave
strictTokenFilter=falsefor broader archive recall. - Enable
strictTokenFilter=truefor datasets where every query word must be visibly supported by the record. - Use
minimumScoreandminimumCommentsfor engagement quality, not semantic relevance. - Add
subredditNamewhen community scope is part of the research question.
Combining all filters can produce a very precise but small or empty dataset. The run summary helps distinguish โthe archive found nothingโ from โthe Actor found candidates but filtered them out.โ
How Archive Search Works
The Actor sends each query and date window through the indexed historical archive path. Search is split into UTC month windows, newest first, and results are saved progressively. Keyword searches use the private monthly full-text indexes. A subredditName without keywords resolves to one of 256 proprietary monthly subreddit buckets, avoiding multi-gigabyte month-wide index downloads and third-party search APIs.
The runtime:
- validates the input and resolves the effective UTC date range;
- searches the relevant monthly keyword index or direct subreddit index;
- normalizes source records into a consistent Reddit post schema;
- applies NSFW, strict-query, score, and comment-count filters;
- removes duplicate Reddit IDs across pages, months, and overlapping queries;
- optionally adds sentiment and content-category fields;
- saves dataset rows and updates
RUN-SUMMARY.
Archive coverage and source-field availability vary by month. A successful empty run means no matching records were found in the searched archive range; it does not prove that the topic never appeared on Reddit.
Input Parameters
Provide at least one phrase in queries or subredditKeywords, or provide subredditName by itself for a complete subreddit listing.
| Parameter | Type | Description | Default |
|---|---|---|---|
queries | string[] | One or more phrases to search for in archived Reddit submissions. When both query fields are populated, this field takes priority. | โ |
sort | string | Order candidates inside each monthly keyword-search batch by relevance, recency, hot engagement, score, or comments. | relevance |
subredditName | string | Subreddit without the r/ prefix. With no keywords, collects every indexed post from that subreddit in the selected date window. | โ |
subredditKeywords | string[] | Optional query list for narrowing a subreddit search. Leave empty for the complete subreddit listing. | โ |
subredditSort | string | Candidate ordering for subreddit-focused runs. Overrides sort when subredditName is present. | relevance |
timeframe | string | Inclusive UTC calendar window used when exact dates are absent: all, year (365 days), month (30 days), week (7 days), or day (current UTC day). | all |
dateFrom | string | Earliest UTC archive date to include, formatted as YYYY-MM-DD. Takes priority over timeframe. | โ |
dateTo | string | Latest UTC archive date to include, formatted as YYYY-MM-DD. | Current UTC date |
strictSearch | boolean | Compatibility control that enables full-query token verification on the indexed archive path. | false |
strictTokenFilter | boolean | Require every word from the originating query to appear in the saved post title, body, or canonical URL. | false |
minimumScore | integer | Only save posts with at least this archived Reddit score. | โ |
minimumComments | integer | Only save posts with at least this reported comment count. | โ |
includeNsfw | boolean | Include posts marked NSFW or 18+. | false |
sentiment_analysis | boolean | Add deterministic sentiment scores, confidence, and labels. | true |
content_analysis | boolean | Add deterministic content-category labels and taxonomy paths when evidence is strong enough. | true |
maxPosts | integer | Maximum number of unique records saved across all queries and month windows. Omit it to continue until the selected archive range is exhausted. | โ |
mcpConnectors | string[] | Optional Apify MCP connector selections. Connector IDs remain shell-owned and are not sent to the archive broker. | [] |
Exact dates are inclusive. If dateFrom is later than dateTo, the Actor completes without searching and records empty_date_window in the run summary.
Strict query matching
Enable strictTokenFilter when precision matters more than recall. The archive first returns indexed candidates; the Actor then verifies that every query token occurs in the normalized title, body, or URL. strictSearch=true enables the same verification as a compatibility alias, so enabling either option is sufficient.
For example, the phrase electric bike requires both electric and bike to be present somewhere in those searchable fields. Records matching only one word are filtered out.
Candidate ordering
Use sort for general keyword searches. When subredditName is set, subredditSort takes priority.
relevance: more query-token coverage first, then newer posts;new: newest posts first;hot: highest combined archived score and comment count first;top: highest archived Reddit score first;comments: highest archivednum_commentsfirst.
Sorting is deterministic within each bounded monthly archive batch. The Actor still traverses archive months newest first and saves progressively, so these controls are not a claim of one global ranking across every selected month.
Engagement filters
Use minimumScore and minimumComments to focus on posts that reached a chosen engagement threshold. The values reflect archived Reddit metadata and may be unavailable or incomplete for some historical records. Missing numeric values are treated as zero for filtering.
Filtered limited runs use bounded archive overscan so the Actor can continue searching for enough qualifying records without turning a small request into an unrestricted backend scan.
Sentiment labels
When sentiment_analysis is enabled, the Actor adds:
sentiment_score: raw lexicon-and-rules score;sentiment_score_normalized: bounded score adjusted for text length;sentiment_confidence: heuristic confidence from0to1;sentiment_label:positive,negative,neutral,mixed, oruncertain.
The analyzer is deterministic and lightweight. It is useful for sorting, triage, and aggregate analysis, but it is not a substitute for domain-specific human review.
Content categories
When content_analysis is enabled, the Actor evaluates the post title and body with supporting signals such as subreddit, flair, and outbound domain.
Confident matches can add:
content_category_labelcontent_category_pathcontent_category_confidencecontent_category_match_type
Classification is intentionally conservative. Category fields remain absent when the record does not contain enough evidence.
Example Inputs
Historical keyword research
{"queries": ["electric bike"],"dateFrom": "2020-01-01","dateTo": "2024-12-31","maxPosts": 500}
Subreddit-focused research
{"subredditName": "technology","subredditKeywords": ["artificial intelligence","AI regulation"],"timeframe": "year","maxPosts": 250}
High-precision engagement search
{"queries": ["electric bike"],"dateFrom": "2023-01-01","dateTo": "2025-12-31","strictTokenFilter": true,"minimumScore": 25,"minimumComments": 10,"includeNsfw": false,"sentiment_analysis": true,"content_analysis": true,"maxPosts": 100}
Lightweight ETL output
{"queries": ["venture capital"],"dateFrom": "2015-01-01","dateTo": "2020-12-31","sentiment_analysis": false,"content_analysis": false,"maxPosts": 1000}
Rolling weekly monitoring
{"queries": ["ExampleBrand","ExampleBrand outage"],"timeframe": "week","includeNsfw": false,"sentiment_analysis": true,"content_analysis": false,"maxPosts": 250}
The effective window is the latest seven inclusive UTC calendar dates ending at dateTo, or the current UTC date when dateTo is omitted.
High-engagement discussions
{"queries": ["remote work"],"dateFrom": "2019-01-01","dateTo": "2024-12-31","minimumScore": 100,"minimumComments": 50,"maxPosts": 500}
Archive-wide community search
{"subredditName": "personalfinance","subredditKeywords": ["emergency fund","credit card debt","first home"],"timeframe": "all","includeNsfw": false,"maxPosts": 1000}
Omitting exact dates with timeframe=all searches from the Actor's earliest configured archive date through the current UTC date. This can span many monthly indexes.
Precision-first compliance research
{"queries": ["AI regulation","artificial intelligence regulation"],"subredditName": "technology","dateFrom": "2023-01-01","dateTo": "2025-12-31","strictTokenFilter": true,"minimumScore": 5,"includeNsfw": false,"sentiment_analysis": true,"content_analysis": true,"maxPosts": 300}
Quick archive availability test
{"queries": ["google"],"dateFrom": "2005-06-01","dateTo": "2005-06-30","maxPosts": 3}
Small, fixed-window inputs are useful for validating an integration before starting a broad historical job.
Understanding Input Interactions
queries and subredditKeywords
- At least one of these lists must contain a non-empty phrase.
- If
queriescontains values, it becomes the effective query list. - If
queriesis empty,subredditKeywordsbecomes the effective query list. - If both keyword lists are empty,
subredditNamebecomes a complete indexed subreddit listing. - With keywords present,
subredditNameacts as an archive filter.
timeframe, dateFrom, and dateTo
dateFromtakes priority over the relativetimeframe.dateTodefaults to the current UTC date.dayselects the current or specifieddateToUTC calendar date.weekselects seven inclusive UTC dates.monthselects 30 inclusive UTC dates.yearselects 365 inclusive UTC dates.allbegins at the Actor's configured archive start date.
Date inputs accept YYYY-MM-DD. Values such as 2025-02-31 are rejected as invalid rather than silently rolled into another month.
Filters and maxPosts
maxPosts counts successfully saved unique records. Candidates rejected by local filters or deduplication do not consume the saved-record limit.
For limited filtered runs, the Actor asks the archive for bounded pages larger than the remaining save limit. It can then remove unsuitable candidates and follow the archive cursor until one of these conditions is met:
- enough unique records have been saved;
- the archive month is exhausted;
- every requested month is exhausted;
- a fatal broker response stops the run.
The inactivity watchdog is scoped to the current monthly shard and renewed after every returned result page. Total shard duration is not the timeout condition. If a shard produces no result page within the inactivity window, the Actor records that month as incomplete and continues with the remaining months instead of discarding the rest of the requested window.
Enrichment controls
sentiment_analysis and content_analysis affect output enrichment only. They do not change which archive records match the query. Disable them when smaller processing overhead or a source-focused schema matters more than built-in analysis.
NSFW behavior
includeNsfw=false excludes records where archived source metadata explicitly marks over_18=true. Historical records without a reliable NSFW flag cannot be inferred as adult content solely from this option.
Optional MCP connectors
mcpConnectors lets you select user-authorized MCP resources through Apify's resource picker. The Actor treats connector IDs as shell-owned configuration and never forwards them to the archive broker or includes them in dataset records. The archive dataset and RUN-SUMMARY remain the authoritative run outputs; the full Reddit dataset is not transmitted through MCP.
Output
Dataset
The default dataset contains one JSON object per unique archived Reddit post. Results can be downloaded from Apify in JSON, JSONL, CSV, Excel, XML, RSS, and other supported formats.
A representative record:
{"kind": "post","query": "electric bike","id": "example123","reddit_fullname": "t3_example123","title": "My experience commuting with an electric bike","body": "After six months of daily use...","author": "example_user","author_url": "https://www.reddit.com/user/example_user/","score": 184,"num_comments": 37,"subreddit": "ebikes","subreddit_url": "https://www.reddit.com/r/ebikes/","created_utc": "2024-04-18T09:30:00.000Z","created_timestamp": 1713432600,"created_date": "2024-04-18","created_year": 2024,"created_month": 4,"url": "https://www.reddit.com/r/ebikes/comments/example123/example/","canonical_url": "https://www.reddit.com/r/ebikes/comments/example123/example/","old_reddit_url": "https://old.reddit.com/r/ebikes/comments/example123/example/","over_18": false,"engagement_total": 221,"word_count": 42,"retrieved_at": "2026-07-23T09:00:00.000Z"}
Fields are included when the archive source or Actor can derive them reliably. Optional fields are omitted rather than filled with misleading placeholders.
Stable identity and deduplication
Use the following idempotency key for upserts:
kind + ":" + id
For example:
post:example123
The Actor applies this identity internally and does not save the same Reddit post twice when it appears across overlapping queries, archive pages, or month windows.
Run summary artifacts
The default key-value store contains:
RUN-SUMMARY: the standard machine-readable run artifact;ARCHIVE_SEARCH_SUMMARY: a backward-compatible alias with the same content.
Important summary fields include:
| Field | Meaning |
|---|---|
queries | Effective archive search phrases. |
subredditName | Applied subreddit filter, or null. |
dateFrom / dateTo | Effective inclusive UTC archive window. |
maxPosts | Requested save limit, or null when omitted. |
recordsFound | Records returned by the archive before local filters and deduplication. |
recordsFiltered | Records removed by NSFW, strict-token, score, or comment-count filters. |
recordsDuplicate | Repeated Reddit identities removed across archive pages or months. |
recordsSaved | Unique dataset rows successfully saved. |
monthsRequested | Number of UTC month windows planned. |
monthsChecked | Number of month windows completed or recorded. |
monthsIncomplete | Number of requested month windows that timed out or had no available index. |
monthSummaries | Per-month and per-page search details. |
stopReason | Why collection ended. |
brokerMetadata | Operational source metadata useful for support. |
traceId | Broker trace identifier when available. |
Common stopReason values:
completed: the selected archive range was exhausted;limit_reached: the Actor saved the requested maximum;empty_date_window:dateFromwas later thandateTo;missing_target: neither a usable keyword nor a subreddit was provided;partial: one or more months timed out or had no available index, while the remaining months continued;failed: invalid input, an invalid broker response, or another fatal error stopped the run.
A representative summary:
{"source": "b2_sqlite_fts","mode": "fast","queries": ["electric bike"],"subredditName": "ebikes","dateFrom": "2024-01-01","dateTo": "2024-12-31","maxPosts": 100,"recordsFound": 146,"recordsSaved": 100,"recordsFiltered": 31,"recordsDuplicate": 15,"stopReason": "limit_reached","monthsRequested": 12,"monthsChecked": 7,"monthSummaries": [{"label": "2024-12","dateFrom": "2024-12-01","dateTo": "2024-12-31","status": "completed","recordsFound": 28,"recordsSaved": 19,"recordsFiltered": 6,"recordsDuplicate": 3,"pages": [{"page": 1,"status": "completed","recordsFound": 28,"recordsSaved": 19,"recordsFiltered": 6,"recordsDuplicate": 3}]}],"brokerMetadata": {"sourceId": "reddit_historical_archive","platform": "apify"},"traceId": null}
Counts in this example are illustrative. Use the summary saved by your run as the authoritative record.
Reading summary counts
The principal count relationship is:
recordsFound-> recordsFiltered removed by configured filters-> recordsDuplicate removed by stable identity-> recordsSaved written to the dataset
The values do not always form a simple subtraction when maxPosts stops a page early, because the archive can return more candidates than the remaining save limit. The dataset count and recordsSaved remain authoritative for delivered rows.
Empty and failed runs
The Actor preserves a summary even when normal rows are not expected:
- missing target: zero rows and
stopReason=missing_target; - inverted date window: zero rows and
stopReason=empty_date_window; - valid search with no candidates: zero rows and
stopReason=completed; - invalid date or broker response: zero or partial rows and
stopReason=failed; - broker deadline or unavailable monthly index: zero or partial rows and
stopReason=partial; later months are still attempted.
This makes scheduled jobs easier to monitor because a zero-row dataset can be interpreted using the accompanying artifact instead of process exit alone.
Field Reference
Identity and provenance
kind(string): Record category. Public archive search currently returnspost.id(string): Stable Reddit post identifier without a kind prefix.reddit_fullname(string): Kind-prefixed Reddit identifier such ast3_example123.query(string): Originating archive phrase associated with this match.archive_source(string, optional): Archive source label supplied by the backend.archive_mode(string, optional): Archive retrieval mode supplied by the backend.archive_shard_id(string, optional): Archive shard or partition identifier.sourceFile(string, optional): Historical source-file identifier.
Content and authorship
title(string): Archived post title.body(string): Normalized self-text or archived body text.body_html(string, optional): HTML-formatted body when available.author(string): Archived Reddit username or a deletion placeholder.author_url(string, optional): Deterministic Reddit profile URL for a usable username.author_fullname(string, optional): Internal kind-prefixed author identifier.author_flair_text(string, optional): Author flair visible on the archived post.author_premium(boolean, optional): Archived premium-status indicator.flair(string, optional): Post link-flair text.
Community and engagement
subreddit(string): Subreddit name withoutr/.subreddit_url(string, optional): Deterministic canonical subreddit URL.subreddit_id(string, optional): Internal kind-prefixed subreddit identifier.subreddit_name_prefixed(string, optional): Display form such asr/technology.subreddit_subscribers(number, optional): Subscriber count present in the archived payload.score(number): Archived Reddit score.upvote_ratio(number, optional): Archived upvote ratio.num_comments(number): Reported post comment count.engagement_total(number):score + num_comments.comment_to_score_ratio(number, optional): Comment count divided by score when score is positive.is_high_engagement(boolean): Convenience flag based on the Actor's high-engagement threshold.score_per_hour(number): Score divided by post age with a small minimum age floor.comments_per_hour(number): Comment count divided by post age with a small minimum age floor.total_awards_received(number, optional): Total awards in the archived payload.all_awardings(array, optional): Raw archived award objects.gilded(number, optional): Archived gilding count.
Dates and analytics
created_utc(string): Normalized ISO creation timestamp.created_timestamp(integer): Unix creation timestamp in seconds.created_date(string): UTC date inYYYY-MM-DDformat.created_year(integer): UTC creation year.created_month(integer): UTC creation month from1to12.created_day(integer): UTC day of month.created_hour_utc(integer): UTC hour from0to23.created_day_of_week(string): English UTC weekday name.retrieved_at(string): ISO time when the Actor normalized the row.age_hours(number): Approximate record age at retrieval time.title_length(integer): Character count of the normalized title.body_length(integer): Character count of the normalized body.word_count(integer): Lightweight word count across title and body.
URLs and media
url(string): Canonical Reddit post URL.permalink(string): Relative Reddit permalink.canonical_url(string): Canonical full Reddit URL.old_reddit_url(string): Equivalent legacy Reddit URL.short_url(string):redd.itshort URL derived from the post ID.url_overridden_by_dest(string, optional): Outbound destination for link posts.outbound_url_host(string, optional): Parsed hostname from the outbound destination.domain(string, optional): Reddit domain field or inferred self-post domain.post_hint(string, optional): Archived Reddit post-type hint.thumbnail(string, optional): Thumbnail URL or archived Reddit thumbnail marker.media(object, optional): Raw media object.secure_media(object, optional): Raw secure-media object.secure_media_embed(object, optional): Secure embed metadata.preview(object, optional): Reddit preview metadata.media_metadata(object, optional): Raw media metadata keyed by media ID.gallery_data(object, optional): Raw Reddit gallery structure.gallery_images(array): Normalized gallery image list.media_assets(array): Normalized media-asset list.media_type(string): Normalized class such astext,link,image,gallery,video, orgif.has_media(boolean): Convenience flag for image, gallery, GIF, or video records.gallery_count(integer): Number of normalized gallery images.
Normalized gallery image fields
Each gallery_images item can include:
media_idcaptionwidthheighturlpreviews
Each media_assets item can include:
typemedia_idmime_typeoriginal_urlpreview_urls
Status and moderation
over_18(boolean, optional): Archived NSFW indicator.spoiler(boolean, optional): Whether the post is marked as a spoiler.locked(boolean, optional): Whether replies were locked.archived(boolean, optional): Archived-state indicator from Reddit.pinned(boolean, optional): Whether the post was pinned.stickied(boolean, optional): Whether the post was stickied.is_self(boolean, optional): Whether the record is a self/text post.is_video(boolean, optional): Reddit video indicator.is_gallery(boolean, optional): Reddit gallery indicator.is_original_content(boolean, optional): Original-content indicator.edited(boolean | number, optional):falsewhen untouched or an edit timestamp.hidden(boolean, optional): Archived hidden-state value when present.distinguished(string, optional): Moderator, administrator, or other distinction.score_hidden(boolean, optional): Whether the archived score was hidden.is_deleted_or_removed(boolean): Conservative flag derived from deletion placeholders and removal metadata.content_flags(array): Normalized flags such as NSFW, spoiler, or locked.num_duplicates(number, optional): Reddit-reported duplicate count.num_crossposts(number, optional): Reddit-reported crosspost count.crosspost_parent_list(array, optional): Raw crosspost-parent records.
Optional enrichment
sentiment_score(number): Raw deterministic sentiment score.sentiment_score_normalized(number): Length-adjusted bounded score.sentiment_confidence(number): Heuristic confidence between0and1.sentiment_label(string):positive,negative,neutral,mixed, oruncertain.content_category_label(string): Most specific confident category label.content_category_path(array): Category hierarchy from broad to specific.content_category_confidence(number): Confidence assigned to the category match.content_category_match_type(string): Evidence type used for the classification.
Data Guarantees and Limitations
- Archived data: Results come from stored historical sources, not live Reddit pages.
- Best-effort coverage: Archive completeness varies by month, subreddit, and source.
- Posts only: The public archive path currently searches submissions and does not collect comments.
- Optional fields: Historical records can lack fields available on newer Reddit payloads.
- UTC dates: Date filtering operates on inclusive UTC calendar dates.
- Deterministic limits:
maxPostsapplies across the entire run, not separately to each query. - Progressive saves: Records are pushed as archive pages complete.
- Deduplicated output: Repeated
kind:ididentities are saved once per run. - Conservative enrichment: Sentiment and category fields are omitted when disabled; category fields can also be absent when evidence is weak.
- Empty results: A zero-row run can mean no archive match, incomplete archive coverage, or filters that excluded all candidates.
Performance
Performance depends primarily on:
- the number of UTC months searched;
- query selectivity;
- whether a subreddit filter is applied;
- how many candidates local filters reject;
- whether enrichment is enabled;
- archive index and broker readiness.
In the local release matrix, both 1,000-row lanes completed in roughly two seconds including Actor startup. A live production-broker smoke saved three requested records from a single indexed month in about eleven seconds. In the complete May 2026 subreddit-bucket pilot, 47,453,180 owned archive posts were partitioned into exactly 256 objects in about 10.1 minutes. The resulting Logitech bucket occupied 31.1 MB, materialized its persistent local cursor index in 1.32 seconds, and returned a 100-record page in under one millisecond. Broad multi-year or unlimited searches can still take longer because more archive windows must be checked.
For quick validation, start with one query, one month, and a small maxPosts. Expand the date range after confirming the query returns the expected kind of records.
Performance planning by workload
Small validation run
Typical shape:
- one query;
- one subreddit or no subreddit;
- one month;
maxPostsbetween 3 and 100.
Use this shape to validate wording, field availability, and integration behavior.
Focused research run
Typical shape:
- one to five related queries;
- one to 12 months;
- optional strict or engagement filtering;
maxPostsbetween 100 and 2,000.
This is a practical range for most interactive research tasks and scheduled reports.
Broad historical run
Typical shape:
- multiple phrases;
- several years or
timeframe=all; - no save limit or a high limit;
- enrichment enabled.
These runs can traverse many monthly indexes and should be treated as batch jobs. Schedule them with enough platform timeout and inspect monthly progress in RUN-SUMMARY.
Why a zero-result query can still take time
Output count is not the same as search work. A selective query that finds three records early can stop quickly when maxPosts=3. A phrase with no archive matches may need to check every selected month before the Actor can confidently finish with zero rows.
Enrichment overhead
Sentiment and content classification run locally after archive candidates are normalized. In the release matrix, the enriched and non-enriched 1,000-row lanes both finished near two seconds including local Actor startup. Real cloud duration is usually dominated by archive access, monthly index work, and storage rather than these deterministic enrichments.
Dataset size considerations
Rich Reddit records can contain media metadata, preview objects, gallery structures, awards, or crosspost data. CSV exports flatten complex values less naturally than JSON or JSONL. For large analytical pipelines:
- prefer JSONL or direct dataset API reads;
- disable enrichment when it is not needed;
- select only required columns downstream;
- use
kind:idas the warehouse upsert key; - partition downstream storage by
created_yearandcreated_month.
Cost and run-size control
Use maxPosts to bound delivered records and start with a narrow date window before scaling. When filters are enabled, the Actor may inspect more candidates than it saves, because rejected and duplicate records do not count toward the save limit.
How to Run on Apify
- Open the Actor in Apify Console.
- Add at least one archive search phrase.
- Optionally choose a subreddit and UTC date window.
- Configure relevance, engagement, NSFW, and enrichment controls.
- Set
maxPostsfor bounded runs. - Click Start.
- Review dataset rows and
RUN-SUMMARYbefore exporting or automating downstream actions.
Scheduling and Automation
Use Apify schedules for recurring research windows and webhooks for downstream processing.
Typical workflows include:
- monthly brand or competitor history reports;
- periodic product-feedback research;
- fixed-window academic or market analysis;
- automatic export into a warehouse or spreadsheet;
- notifications when a run completes or fails.
For recurring jobs, prefer fixed dateFrom and dateTo values when reproducibility matters. Relative windows are useful for rolling reports but naturally change between runs.
Export and Integration Options
Apify dataset API
Use the default dataset API for programmatic reads after a run. The Actor output schema exposes a direct results link, and Apify supports common options for format, field selection, cleaning, pagination, and downloads.
Recommended pipeline pattern:
- wait for the Actor run to reach a terminal state;
- read
RUN-SUMMARY; - confirm
stopReasonandrecordsSaved; - fetch dataset items;
- upsert by
kind:id; - persist the run ID and summary alongside the imported batch.
Webhooks
Configure Apify webhooks to notify a downstream service when runs succeed, fail, time out, or are aborted. The receiving workflow should inspect both the platform run status and RUN-SUMMARY.
Schedules
Schedules are useful for:
- weekly rolling mention research;
- monthly fixed-window exports;
- quarterly category analysis;
- repeatable competitor comparisons;
- periodic archive backfills.
Avoid overlapping scheduled windows unless your downstream system performs identity-based upserts. The Actor deduplicates within one run, while downstream storage must deduplicate across separate runs.
Google Sheets and spreadsheets
Spreadsheet exports work well for smaller review datasets. Prefer a focused field set such as:
queryidtitlebodyauthorscorenum_commentssubredditcreated_utccanonical_urlsentiment_labelcontent_category_label
Large media objects and nested arrays are better handled in JSON-oriented tools.
Warehouses and databases
Suggested modeling:
- primary key:
kind:id; - event time:
created_utc; - ingestion time:
retrieved_at; - partitions:
created_year,created_month; - community dimension:
subreddit; - query attribution:
query; - engagement measures:
score,num_comments,engagement_total; - optional NLP dimensions:
sentiment_label,content_category_label.
Search and vector systems
For full-text or semantic search, a useful document string is:
title + "\n\n" + body
Keep id, canonical_url, subreddit, created_utc, score fields, and archive provenance as metadata. Do not treat Actor sentiment or taxonomy output as the only interpretation of the text.
Dashboards and BI tools
Useful aggregate views include:
- records by month;
- records by subreddit;
- average score and comments;
- sentiment distribution;
- category distribution;
- high-engagement share;
- top outbound domains;
- query-to-subreddit distribution.
Use recordsFiltered and recordsDuplicate from run summaries to monitor whether an input is becoming too restrictive or repetitive over time.
Recommended Production Patterns
Reproducible historical batch
- Use exact
dateFromanddateTo. - Store the input JSON with the exported dataset.
- Store
RUN-SUMMARY. - Use a bounded
maxPostsor document that the range was intentionally exhausted. - Record archive limitations with the analysis.
Rolling monitoring job
- Use
timeframe=weekortimeframe=month. - Schedule at a predictable UTC time.
- Upsert by
kind:id. - Expect overlap between adjacent rolling windows.
- Track
recordsSavedand stop reasons for operational alerts.
High-precision review queue
- Enable
strictTokenFilter. - Add moderate score or comment thresholds.
- Keep enrichment enabled.
- Use a smaller limit.
- Review the first dataset rows before expanding the range.
Broad discovery corpus
- Leave strict matching disabled.
- Avoid engagement thresholds initially.
- Use several focused phrases.
- Export JSONL.
- Deduplicate across separate runs in downstream storage.
- Apply custom NLP after collection when domain-specific interpretation is important.
Responsible Use
This Actor processes publicly sourced historical Reddit content. Users are responsible for ensuring their collection and use comply with applicable laws, regulations, organizational policies, Reddit's terms, and relevant data-protection requirements.
Recommended practices:
- collect only the data needed for a legitimate purpose;
- avoid using archived content for harassment, spam, or invasive profiling;
- protect usernames and other personal information appropriately;
- honor deletion, retention, and access requirements that apply to your use case;
- validate important conclusions against source context and archive limitations.
Troubleshooting
The run saved no records
- Confirm that at least one phrase exists in
queriesorsubredditKeywords, or entersubredditNamefor a complete subreddit listing. - Try a broader phrase or remove
strictTokenFilter. - Lower
minimumScoreorminimumComments. - Expand the date range.
- Remove the subreddit filter to search across available communities.
- Inspect
recordsFound,recordsFiltered, andstopReasoninRUN-SUMMARY.
The summary reports partial
Check monthsIncomplete and monthSummaries to see exactly which monthly indexes timed out or were unavailable. The Actor continues through the rest of the requested window, so the dataset remains usable without being presented as complete.
Fewer records were saved than requested
The selected archive range may have been exhausted, filters may have removed candidates, or repeated Reddit IDs may have been deduplicated. Compare recordsFound, recordsFiltered, recordsDuplicate, and recordsSaved.
Some fields are missing
Historical Reddit payloads vary. Optional values are omitted when the source does not provide them or when enrichment is disabled or inconclusive.
A saved input contains live-scraper fields
Fields for direct URLs, live ranking, comment scraping, or maximum-coverage crawling are ignored by archive search. The Actor emits a warning listing any recognized legacy fields that do not affect the archive path.
Frequently Asked Questions
Does this Actor scrape Reddit live?
No. It searches stored historical archive indexes. Use a live Reddit scraper when you need current pages, direct URLs, current comments, or Reddit's present ranking behavior.
Does it collect comments?
No. The public archive path currently requests Reddit submissions only. num_comments is the archived comment count reported on a post, not a collection of comment records.
Do I need exact dates?
No. A query can run with timeframe=all, and dateTo defaults to the current UTC date. Exact dates are recommended for reproducible research and smaller, more predictable jobs.
How far back does the archive go?
The Actor's configured search start is June 2005, but actual coverage varies by month and source. The presence or absence of a record should not be interpreted as proof of complete Reddit coverage.
Is maxPosts applied per query?
No. It is the maximum number of unique records saved across the entire run, including all queries and monthly windows.
Why can recordsFound exceed maxPosts?
The archive can return a page of candidates larger than the remaining save limit. Local filters and deduplication run before final rows are capped and saved.
Why is recordsSaved smaller than recordsFound?
Candidates may be removed because they are NSFW, do not satisfy strict token matching, fall below engagement thresholds, repeat an existing Reddit identity, or arrive after the save limit has been reached.
What does strict token matching do?
It tokenizes the originating query and requires every token to appear in the normalized title, body, or canonical URL. It is a local precision filter applied after archive retrieval.
Does strict matching support an exact phrase?
No. It requires all tokens, but they can appear in different searchable fields or positions. Exact-phrase matching is not currently a public input mode.
Does minimumComments download comments?
No. It filters posts using the archived num_comments value.
Are scores and comment counts current?
No. They reflect the metadata captured in the historical source record and may differ from values visible on Reddit today.
Are sentiment fields generated by an external AI API?
No. The Actor uses a deterministic lexicon-and-rules analyzer. This keeps output repeatable and avoids sending post text to a separate AI provider for sentiment.
Why is the category missing when content analysis is enabled?
The classifier is conservative. It leaves fields absent when title, body, subreddit, flair, and domain signals do not provide enough evidence for a reliable category.
Can the same post appear under multiple queries?
The same post can match multiple queries, but it is saved once per run using the kind:id identity. recordsDuplicate counts repeated identities that were removed.
Can I include NSFW content?
Yes. Set includeNsfw=true. Keep it disabled when adult content is unnecessary or unsuitable for the intended downstream system.
What happens if I omit maxPosts?
The Actor continues until the selected archive range is exhausted or the run stops because of an error, timeout, or platform limit. No arbitrary hidden result cap is added by the Actor.
What happens if the date range is reversed?
The Actor saves no normal rows, writes a summary with stopReason=empty_date_window, and completes without sending an archive request.
What happens if the date itself is invalid?
An impossible date such as 2025-02-31 fails validation during runtime, writes a failure summary, and causes the Actor execution to fail.
How should I monitor scheduled runs?
Check the platform run state and read RUN-SUMMARY. Alert on unexpected failed or partial reasons, non-zero monthsIncomplete, unusual drops in recordsSaved, or large increases in filtered and duplicate counts.
Which export format should I use?
- Use JSON or JSONL for complete nested records.
- Use CSV or Excel for smaller human-review datasets with a selected column set.
- Use the dataset API for automated pipelines.
Can I use the data for AI training?
That depends on your legal basis, policies, Reddit's applicable terms, data-protection obligations, and the nature of the training use. The Actor provides collection tooling, not permission or legal advice.
Why might a known Reddit post be absent?
Possible reasons include incomplete archive coverage, a missing month or shard, query mismatch, subreddit filtering, local filters, deleted or altered historical source data, or the post being outside the selected date range.
Is an empty successful run an error?
Not necessarily. A successful run with recordsSaved=0 means the Actor completed its planned search without delivering a qualifying row. Inspect recordsFound and recordsFiltered to understand whether the archive returned candidates.
Support
For help, use the Issues tab or the Actor page on Apify. Include:
- the Apify run ID;
- the exact input with sensitive values removed;
- the expected result and actual result;
- the
RUN-SUMMARYrecord; - a small dataset sample when available.
