Reddit Posts Scraper avatar

Reddit Posts Scraper

Pricing

$0.30 / 1,000 results

Go to Apify Store
Reddit Posts Scraper

Reddit Posts Scraper

Collect public Reddit post records and paginated comment trees from a public archive. Export post details, comment text, scores, timestamps, parent IDs, depth, and coverage indicators.

Pricing

$0.30 / 1,000 results

Rating

0.0

(0)

Developer

MLG Data

MLG Data

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

12 days ago

Last modified

Share

Scrape public Reddit posts and their comment trees from post links, short links, IDs, or keyword searches. Export Reddit post and comment data to JSON, CSV, or Excel with text, timestamps, scores, reply relationships, and clear indicators of how much of the archived discussion was scanned. The source is a public archive snapshot, so the returned values are historical records rather than live counts of every current comment.

Each post produces one post row followed by its comment rows. The shared _post_id joins every comment to its post. Comment rows include parentId and depth when the required ancestors are present in the scanned records. This structure supports bulk thread exports without requiring a separate nested file for each post.

What data can you extract from Reddit?

The dataset is flat. The _type field identifies post or comment, and fields that belong only to the other row type are absent. Null means the public archive did not provide a value. The table documents every output field.

FieldDescriptionExample
_typeRow kindcomment
_post_idParent post ID with t3_ prefixt3_1k2vnz0
_statusLookup status for a returned recordfound
idPost or comment ID with type prefixt1_mnx52zk
postUrlFull link to the parent posthttps://www.reddit.com/r/.../comments/1k2vnz0/.../
titlePost headlineMark and Helly's relationship is kinda strange
subredditCommunity nameexamplecommunity
authorPublic handle in the archivea public handle
authorIdPublic account ID when presentt2_...
authorAccountTypeAccount type unavailable in the archivenull
authorIsCakeDayAccount anniversary unavailable in the archivenull
authorIconAccount icon unavailable in the archivenull
languageCodeComment language unavailable in the archivenull
scoreScore recorded in the archived record1
upvoteRatioPost upvote ratio in the snapshot1
commentCountCount stored on the archived post1
collectedCommentCountComment rows selected by the run150
archiveExhaustedWhether scanning ended on a partial or empty archive pagefalse
createdAtUTC creation time2025-04-19T12:56:59+00:00
archivedAtUTC archive retrieval time, if presentnull
permalinkRelative post or comment link/r/.../comments/.../
urlPost destination, including an outbound link if applicablehttps://www.reddit.com/...
selfTextMarkdown body of a text postpost text
domainDestination domain recorded for the postself.examplecommunity
isSelfWhether the post is a text posttrue
isVideoWhether the post is a videofalse
isNsfwAdult-content markerfalse
isSpoilerSpoiler markerfalse
isStickiedWhether the post or comment is pinnedfalse
isLockedWhether replies are lockedfalse
isArchivedWhether the record was marked archivedfalse
flairPost flair textDiscussion
awardsArchived award count0
subredditSubscribersArchived community subscriber count818744
thumbnailThumbnail URL or a text markerself
editedAtUTC comment edit time, when recordednull
depthReply level; null if an ancestor was outside the scan0
parentIdParent comment ID; null for a top-level commentt1_mnx52zk
bodyComment text in markdowncomment text
isScoreHiddenWhether the comment score was hiddenfalse
distinguishedAsPublic moderation distinctionmoderator
authorFlairPublic commenter flairnull
isDeletedWhether archived author or text indicates deletionfalse
isRemovedWhether the archived record indicates removalfalse
removedByCategoryRemoval category, if recordednull
childCountDirect replies found in scanned records2
isInitiallyCollapsedWhether the archived comment was collapsedfalse
controversialityControversy signal stored in the comment0
isCommercialCommunicationCommercial flag unavailable in the archivenull
contentTypeHintContent format hint unavailable in the archivenull
isOpWhether the commenter matches the post authorfalse

A post row and a comment row share routing fields but do not share all content fields. For example, title belongs to the post, while body, depth, and parentId belong to comments. When exporting a table, filter _type before calculating fill rates for a field that exists on only one row type.

The snapshot can contain inconsistent counts. In the verified example, the archived post stored commentCount equal to one, while a scan returned 150 comment rows. Use collectedCommentCount for the rows actually emitted by the run. Treat commentCount as historical metadata, not as a completeness guarantee.

How to scrape Reddit posts and comments

  1. Paste a public post URL or ID into the single-post field. For a batch, add entries to the list or supply a URL file.
  2. Select a comment order and set the comments-per-post limit. The default is 200; zero keeps every comment found within the scan limit.
  3. Increase the archive scan when you need a broader pool for sorting, Q&A filtering, or reply depth. The extended scan checks up to 10,000 archived comments per post.
  4. Run the actor. A post row appears first for each found post, followed by selected comments.
  5. Download the default dataset as JSON, CSV, or Excel. Group rows by _post_id and use parentId to link replies.

You can supply the bare ID 1k2vnz0, the prefixed ID t3_1k2vnz0, a full post link, or a redd.it short link. The actor normalizes those forms to the same post ID. Duplicate IDs within a run are collected once. An invalid ID stops at input validation instead of being silently treated as a different post.

A keyword search is used only when the post URL fields are empty. Each query finds up to maxPostsPerQuery archived submissions. The actor then collects comments for those submissions. Search results reflect the archive's searchable text and snapshot coverage; a current post may have no archived record.

Input

ParameterTypeDefaultDescription
postUrlstringemptyOne public post link, short link, or ID.
postUrlsstring array[]Multiple post links or IDs.
postUrlsFilestringemptyHTTPS TXT or CSV URL, up to 5 MB.
queriesstring array[]Keyword searches used when no URL input is provided.
maxPostsPerQueryinteger25Maximum discovered posts for each keyword.
sortstringbestbest, top, new, old, controversial, or qa.
filterstringemptyAll threads, answered, or unanswered.
maxCommentsPerPostinteger200Comment rows emitted per post; zero means all scanned rows.
expandAllCommentsbooleanfalseRaise the scan to at least 10,000 archived comments.
maxScanPerPostinteger1000Upper bound for the scanned comment pool before sorting.
maxItemsinteger0Hard cap for all dataset rows; zero means no output cap.

For a single discussion, start with a modest output cap:

{
"postUrl": "1k2vnz0",
"sort": "old",
"maxCommentsPerPost": 150,
"maxScanPerPost": 200
}

For a larger batch, place IDs and links in postUrls. A TXT file must contain one value per line. A CSV file must have a url or permalink column. The file URL must use HTTPS. If both direct post inputs and queries are supplied, direct inputs take precedence. This keeps an existing list from unexpectedly expanding into a search run.

The sort option applies after comments are scanned. New and old use archived creation timestamps. Top and best use archived score; best is an approximation of the live ordering because the archive does not expose the live ranking calculation. Controversial uses the archived controversy signal, with score as a tie breaker. Q&A places comments from the post author first. These orders describe the selected archived pool, not all comments on the live site.

For Q&A filtering, answered keeps scanned top-level threads with a reply from the post author. Unanswered keeps other scanned threads. A reply outside the scanned pool is invisible to this filter. Increase the scan when the result will drive analysis of answered versus unanswered questions.

Output example

The following trimmed post row comes from a successful run. That run returned 151 rows: one post and 150 comments. The full row also contains post text, flags, flair, and other fields in the table above.

{
"_type": "post",
"_post_id": "t3_1k2vnz0",
"_status": "found",
"id": "t3_1k2vnz0",
"title": "Mark and Helly's relationship is kinda strange",
"score": 1,
"commentCount": 1,
"collectedCommentCount": 150,
"archiveExhausted": false,
"createdAt": "2025-04-19T12:56:59+00:00"
}

The archiveExhausted value is false because the run stopped at its configured scan limit. It does not mean 150 comments are all comments ever posted. A true value means the archive endpoint returned an empty or partial page during this run; it still does not prove that the archive captured every live comment.

To rebuild a thread, index comment rows by id. A row with parentId null is a top-level comment. For a nested row, look up its parentId in that index. Depth is zero for top-level comments, one for their replies, and so on. If an ancestor was not scanned, depth is null instead of an invented level. The original parentId is still available so the gap is visible.

Use cases

  • Discussion research: collect public comments from a known set of posts and keep reply structure for context. Sort by time to see how the conversation developed.
  • Community feedback: search a topic in the archive, then read post text and comment bodies together. Score and timestamp fields help prioritize records for manual review.
  • Question analysis: use Q&A order with the answered filter to inspect discussion branches that received a post-author reply within the scanned pool.
  • Content tracking: run the same post IDs on a schedule and compare archived fields or selected comments across runs. Archive snapshots may update at different times.
  • Bulk export: send a TXT or CSV list of public post IDs and receive one flat dataset suitable for joins by _post_id.
  • Reply network analysis: use id, parentId, depth, and isOp to describe branching and direct author participation, while retaining null depths for missing ancestors.

How much does it cost to scrape Reddit?

The configured result price is $0.30 per 1,000 emitted rows. A row is either one post or one comment. The actor does not charge separately for the two row types. The output limit and pay-per-event budget stop are checked as rows are pushed to the dataset.

A run with one post and 150 comments emits 151 rows, equivalent to $0.0453 at the configured event price. A batch that emits 1,000 combined rows is $0.30. A larger export of 5,000 combined rows is $1.50. These examples count delivered rows; scan requests that produce no row do not add a result event. The displayed platform bill can include account-specific usage outside the event amount.

Set maxCommentsPerPost and maxItems to keep a small trial predictable. MaxItems applies across all posts and comments, so a cap of 100 can end in the middle of a post's comment set. If you need complete per-post sets, use a cap large enough for your configured post count or leave the total cap at zero and control each post with maxCommentsPerPost.

Tips for best results

Use a known public post ID for a first run. A direct ID lookup requires less archive search work than a keyword query and avoids collecting unrelated posts. For batches, deduplicate links before upload if you want the input list to match your own accounting, although the actor also deduplicates normalized IDs.

Increase maxScanPerPost when using score order or Q&A filters. Sorting only considers the scanned pool, so a small scan can omit higher-score comments that appeared later in the archive. ExpandAllComments raises the scan to at least 10,000 records per post. It is useful for large discussions, but it can take longer and cannot restore records the archive never saved.

Use old for a chronological sample near the beginning of a thread. Use new when recent archived comments matter. Compare createdAt with archivedAt when the archive provides both. Some archived records have no retrieval timestamp, and that absence is shown as null. Do not infer a precise snapshot time from a null value.

Watch archiveExhausted, collectedCommentCount, and the selected limit together. The first says whether pagination ended before the configured scan cap, the second says how many comment rows the run actually emitted, and the limit explains an intentional stop. The stored commentCount may disagree with both because it came from a different historical snapshot.

For bulk file input, use a simple UTF-8 TXT file or a CSV with a clear header. Avoid mixing unrelated fields into the URL column. The actor accepts a hosted HTTPS file up to 5 MB. A bad line is treated as an invalid input value, so clean lists are preferable for unattended schedules.

Limits

Direct Reddit pages, thread JSON, listing JSON, feeds, and a desktop comments route were blocked by access challenges in the test environment. The working source is a public archive JSON endpoint. As a result, this actor describes archived public content. It cannot guarantee real-time fields, live scores, current removal state, or every comment in a thread. A post absent from the archive is skipped, and a run with no returned posts fails visibly.

The archive's submission metadata can be stale relative to its comment records. In the verified run, the archived post's commentCount was one while 150 archived comments were emitted. Use the fields according to their definitions rather than treating the post count as a validation total. An archived score of one also should not be read as a current score.

Pagination advances by creation time and deduplicates comment IDs. A dense group of comments with exactly the same creation timestamp at a page boundary can leave a gap if the archive does not provide a more precise cursor. When a parent comment falls outside the scanned pool, its descendants keep parentId but have null depth. ChildCount counts only direct children found during that scan.

Deleted and removed text cannot be recovered if the archive only holds a placeholder. Public account icons, account anniversary flags, and language labels are not exposed by the chosen source and are not invented in output. Some archived fields are null or absent by design. Private communities, login-only discussions, and personal content outside public records are outside this actor's scope.

FAQ

Can I use a full URL and a bare ID in one batch?

Yes. Put both in postUrls. Full post links, redd.it links, bare IDs, and t3_ IDs are normalized. If two values identify the same post, the run processes that ID once.

Does extended scanning guarantee every reply?

No. It raises the archive scan to at least 10,000 records and follows pages until a partial page or limit. The archive may lack records, and comments beyond the scan cap are omitted. ArchiveExhausted reports how pagination ended, not live-site completeness.

Is the best order identical to the live site?

No. Best sorts the scanned archive pool by its stored score. The live ranking calculation is not present in the archive. Use top or old when you want an easier-to-interpret ordering.

Why can commentCount be smaller than returned comment rows?

The post and comments can be snapshots from different times. CommentCount is copied from the post record. CollectedCommentCount is calculated from rows selected in this run and is the useful count for exported data.

What happens when a field is empty?

Null or absence means the public archive did not supply the value, or the field belongs to the other row type. A missing edit time does not imply a missing creation time. A missing depth means the scanned records did not contain enough ancestry to calculate it safely.

Can I schedule runs and export a spreadsheet?

Yes. Scheduled runs use the same input as a manual run. The default dataset can be downloaded as JSON, CSV, or Excel. Use _type to separate posts from comments and _post_id to reconnect them.

This actor processes public archived records. Follow the site's terms and applicable privacy law, avoid misuse of personal data, and apply your own retention rules for exported content. A public username is still data about a person.

Integrations

Run this actor through the platform interface or API, export the default dataset, and connect the results to webhooks, scheduled jobs, spreadsheets, or downstream workflows. The flat schema makes it practical to append new runs to a table, filter on _type, and join comment rows to posts by _post_id. If a workflow needs a full nested thread, build it from id and parentId after export and keep null depth as a signal of missing ancestry.

Support

Open an issue on the Issues tab; we reply within 24h and add fields on request.