Reddit Posts Scraper
Pricing
$0.30 / 1,000 results
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
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
12 days ago
Last modified
Categories
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.
| Field | Description | Example |
|---|---|---|
| _type | Row kind | comment |
| _post_id | Parent post ID with t3_ prefix | t3_1k2vnz0 |
| _status | Lookup status for a returned record | found |
| id | Post or comment ID with type prefix | t1_mnx52zk |
| postUrl | Full link to the parent post | https://www.reddit.com/r/.../comments/1k2vnz0/.../ |
| title | Post headline | Mark and Helly's relationship is kinda strange |
| subreddit | Community name | examplecommunity |
| author | Public handle in the archive | a public handle |
| authorId | Public account ID when present | t2_... |
| authorAccountType | Account type unavailable in the archive | null |
| authorIsCakeDay | Account anniversary unavailable in the archive | null |
| authorIcon | Account icon unavailable in the archive | null |
| languageCode | Comment language unavailable in the archive | null |
| score | Score recorded in the archived record | 1 |
| upvoteRatio | Post upvote ratio in the snapshot | 1 |
| commentCount | Count stored on the archived post | 1 |
| collectedCommentCount | Comment rows selected by the run | 150 |
| archiveExhausted | Whether scanning ended on a partial or empty archive page | false |
| createdAt | UTC creation time | 2025-04-19T12:56:59+00:00 |
| archivedAt | UTC archive retrieval time, if present | null |
| permalink | Relative post or comment link | /r/.../comments/.../ |
| url | Post destination, including an outbound link if applicable | https://www.reddit.com/... |
| selfText | Markdown body of a text post | post text |
| domain | Destination domain recorded for the post | self.examplecommunity |
| isSelf | Whether the post is a text post | true |
| isVideo | Whether the post is a video | false |
| isNsfw | Adult-content marker | false |
| isSpoiler | Spoiler marker | false |
| isStickied | Whether the post or comment is pinned | false |
| isLocked | Whether replies are locked | false |
| isArchived | Whether the record was marked archived | false |
| flair | Post flair text | Discussion |
| awards | Archived award count | 0 |
| subredditSubscribers | Archived community subscriber count | 818744 |
| thumbnail | Thumbnail URL or a text marker | self |
| editedAt | UTC comment edit time, when recorded | null |
| depth | Reply level; null if an ancestor was outside the scan | 0 |
| parentId | Parent comment ID; null for a top-level comment | t1_mnx52zk |
| body | Comment text in markdown | comment text |
| isScoreHidden | Whether the comment score was hidden | false |
| distinguishedAs | Public moderation distinction | moderator |
| authorFlair | Public commenter flair | null |
| isDeleted | Whether archived author or text indicates deletion | false |
| isRemoved | Whether the archived record indicates removal | false |
| removedByCategory | Removal category, if recorded | null |
| childCount | Direct replies found in scanned records | 2 |
| isInitiallyCollapsed | Whether the archived comment was collapsed | false |
| controversiality | Controversy signal stored in the comment | 0 |
| isCommercialCommunication | Commercial flag unavailable in the archive | null |
| contentTypeHint | Content format hint unavailable in the archive | null |
| isOp | Whether the commenter matches the post author | false |
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
- 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.
- Select a comment order and set the comments-per-post limit. The default is 200; zero keeps every comment found within the scan limit.
- 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.
- Run the actor. A post row appears first for each found post, followed by selected comments.
- 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
| Parameter | Type | Default | Description |
|---|---|---|---|
| postUrl | string | empty | One public post link, short link, or ID. |
| postUrls | string array | [] | Multiple post links or IDs. |
| postUrlsFile | string | empty | HTTPS TXT or CSV URL, up to 5 MB. |
| queries | string array | [] | Keyword searches used when no URL input is provided. |
| maxPostsPerQuery | integer | 25 | Maximum discovered posts for each keyword. |
| sort | string | best | best, top, new, old, controversial, or qa. |
| filter | string | empty | All threads, answered, or unanswered. |
| maxCommentsPerPost | integer | 200 | Comment rows emitted per post; zero means all scanned rows. |
| expandAllComments | boolean | false | Raise the scan to at least 10,000 archived comments. |
| maxScanPerPost | integer | 1000 | Upper bound for the scanned comment pool before sorting. |
| maxItems | integer | 0 | Hard 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.
Is collecting this data legal?
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.