# Reddit Historical Archive Scraper for Posts and Comments (`maximedupre/reddit-historical-archive-scraper`) Actor

Collect public Reddit posts and comments from historical date ranges. Search by subreddit, author, post ID, or Reddit URL, then filter by type, phrase, score, and order. Get text, links, dates, engagement, and archive details when available.

- **URL**: https://apify.com/maximedupre/reddit-historical-archive-scraper.md
- **Developed by:** [Maxime Dupré](https://apify.com/maximedupre) (community)
- **Categories:** Social media, News, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.01 / 1,000 archived contents

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

### 🗃️ Find Reddit's public history

Researchers, journalists, analysts, and developers can use Reddit Historical Archive Scraper to find public Reddit posts and comments from past date ranges. It saves structured rows with text, authors, subreddits, dates, scores, links, and archive details when the source provides them. Use those rows to study older discussions, compare communities over time, or prepare text for search and retrieval.

**Try these workflows**

- Collect historical data for a chosen date range with **[Reddit Historical Data Scraper](https://apify.com/maximedupre/reddit-historical-archive-scraper/examples/reddit-historical-data-scraper)**.
- Search a phrase in old public content with **[Reddit Archive Search](https://apify.com/maximedupre/reddit-historical-archive-scraper/examples/reddit-archive-search)**.
- Find a user's posts or comments with **[Reddit Author Scraper](https://apify.com/maximedupre/reddit-historical-archive-scraper/examples/reddit-author-scraper)**.
- Collect one community's posts with **[Reddit Subreddit Scraper](https://apify.com/maximedupre/reddit-historical-archive-scraper/examples/reddit-subreddit-scraper)**.
- Look for topic matches in comments with **[Reddit Comment Scraper](https://apify.com/maximedupre/reddit-historical-archive-scraper/examples/reddit-comment-scraper)**.
- Collect post rows for review with **[Reddit Post Scraper](https://apify.com/maximedupre/reddit-historical-archive-scraper/examples/reddit-post-scraper)**.

#### 📚 Archived Reddit posts and comments

Each saved row is one public archived post or comment. The `recordType` field shows which shape the row uses. Posts can include title, body text, author, subreddit, timestamps, URLs, scores, flair, media, moderation state, and archive provenance. Comments can include text, author, subreddit, timestamps, URL, score, flair, parent and related-post links, moderation state, and archive provenance. Optional fields appear when the archive supplies them.

**Useful facts**

- The stable Reddit `id` helps you join a post or comment with other data.
- The public `url` gives you a source page to review.
- `provenance` can include the archive name and known time and volume coverage.
- `text` is normalized for embedding or retrieval when post body text is available.

#### ▶️ Search the archive in a few steps

**Run steps**

1. Choose `posts`, `comments`, or `both` as the result type.
2. Choose one discovery method: `subreddit`, `author`, `post`, or `url`.
3. Enter one or more values for that target.
4. Add date bounds, a search phrase, a minimum score, or a result order when needed.
5. Use the two limits for a smaller first run, then start the Actor and open the dataset.

A narrow date range and a small limit leave less data to review. The form uses one discovery method per run. Use separate runs for different search setups, while lists of values of the same kind remain supported.

#### ⚙️ Input

**Input fields**

| Field | Type | What it does |
| --- | --- | --- |
| `resultType` | string | Chooses `posts`, `comments`, or `both`. |
| `discoveryMethod` | string | Chooses `subreddit`, `author`, `post`, or `url` as the target kind. |
| `subredditNames` | array of strings | Adds one or more subreddit names without the `r/` prefix. |
| `authorNames` | array of strings | Adds one or more Reddit usernames without the `u/` prefix. The Actor searches posts, comments, or both for each author. |
| `postIds` | array of strings | Adds one or more Reddit post IDs. The Actor retrieves matching archived posts or their comments in the date range. |
| `redditUrls` | array of objects | Adds one or more public Reddit URL objects for archived targets. |
| `redditUrls[].url` | string | A full public Reddit URL that starts with `https://`. |
| `startDate` | string | An optional lower UTC creation-date bound. Records created before it are skipped. Use `YYYY-MM-DD`. |
| `endDate` | string | An optional upper UTC creation-date bound. Records created after it are skipped. Use `YYYY-MM-DD`. |
| `searchPhrase` | string | An optional phrase checked in post titles and body text or in comment body text. With `both`, it is checked in both types. |
| `minScore` | integer | An optional minimum engagement score. Records with a lower score are skipped. |
| `sortOrder` | string | Chooses `newest` or `oldest` creation order. |
| `maxCommentsPerPost` | integer | Limits comments for post ID or Reddit URL targets when comments are selected. Leave it empty to return all available comments until the source is exhausted. |
| `maxItems` | integer | Limits returned posts or comments in the run. Leave it empty to return all available records until the source is exhausted. The schema has no fixed upper bound. |

The required fields are `resultType` and `discoveryMethod`. Dates use UTC.

**Input example**

This is the public input from a successful run.

```json
{
  "resultType": "posts",
  "discoveryMethod": "subreddit",
  "subredditNames": [
    "AskReddit"
  ],
  "sortOrder": "newest",
  "maxItems": 100
}
```

#### 🧾 Output

The Output panel links to the dataset. Each dataset item uses one of the two shapes below, and `recordType` tells you which one it is. Optional fields are present when the archive supplies them.

**Post rows**

| Field | Type | What it does |
| --- | --- | --- |
| `recordType` | string | Identifies this shape as `post`. |
| `id` | string | Gives the stable Reddit ID for joining this post with other data. |
| `title` | string | Gives the post title. |
| `text` | string | Gives normalized post body text when body text is available. |
| `author` | object | Groups public author details when available. |
| `author.name` | string | Gives the public Reddit username of the author. |
| `subreddit` | string | Gives the Reddit community where the post was published. |
| `createdAt` | string | Gives the UTC creation time. |
| `editedAt` | string | Gives the UTC edit time when the archive supplies it. |
| `url` | string | Gives the public Reddit URL for the post. |
| `score` | integer | Gives the engagement score recorded by the archive. |
| `upvoteRatio` | number | Gives the archive's upvote ratio from 0 to 1. |
| `commentCount` | integer | Gives the number of comments recorded for the post. |
| `flair` | string | Gives the post flair when available. |
| `media` | array of objects | Lists media attached to the post when available. |
| `media[].type` | string | Gives the source media type for one media item. |
| `media[].url` | string | Gives the public URL for one media item. |
| `moderation` | object | Groups moderation states recorded for the post. |
| `moderation.removed` | boolean | Says whether the archive marks the post as removed. |
| `moderation.locked` | boolean | Says whether the archive marks the post as locked. |
| `moderation.archived` | boolean | Says whether the archive marks the post as archived on Reddit. |
| `moderation.stickied` | boolean | Says whether the archive marks the post as stickied. |
| `provenance` | object | Groups the archive source and coverage facts for the post. |
| `provenance.archiveName` | string | Gives the name of the public archive that supplied the post. |
| `provenance.coverage` | object | Groups known time and volume coverage for the target. |
| `provenance.coverage.earliestCreatedAt` | string | Gives the earliest creation time covered for the target. |
| `provenance.coverage.latestCreatedAt` | string | Gives the latest creation time covered for the target. |
| `provenance.coverage.availableRecordCount` | integer | Gives the number of records the archive reports for the target. |

**Genuine post row**

```json
{
  "recordType": "post",
  "id": "kmtys6",
  "title": "What made the Cambodian Genocide a genocide?",
  "subreddit": "AskHistorians",
  "createdAt": "2020-12-30T03:28:06.000Z",
  "url": "https://www.reddit.com/r/AskHistorians/comments/kmtys6/what_made_the_cambodian_genocide_a_genocide/",
  "provenance": {
    "archiveName": "arctic-shift.photon-reddit.com",
    "coverage": {
      "earliestCreatedAt": "2020-12-30T03:28:06.000Z",
      "latestCreatedAt": "2020-12-30T03:28:06.000Z",
      "availableRecordCount": 1
    }
  },
  "text": "I understand why a lot of other genocides qualify as genocides since they are one group trying to wipe out another. But the Cambodian Genocide seems different to me in that that doesn’t seem to be the case. Based on what I have read about it, it seems like what happened in Cambodia was a mass killing by the government of its own people. That’s certainly horrible but that just sounds like mass murder rather than genocide. Was there an effort to wipe out non-Cambodians at the time that I am not aware of?",
  "author": {
    "name": "historydude420"
  },
  "score": 3,
  "upvoteRatio": 0.6700000166893005,
  "commentCount": 8,
  "moderation": {
    "locked": false,
    "archived": false,
    "stickied": false
  }
}
```

**Comment rows**

| Field | Type | What it does |
| --- | --- | --- |
| `recordType` | string | Identifies this shape as `comment`. |
| `id` | string | Gives the stable Reddit ID for joining this comment with other data. |
| `text` | string | Gives normalized comment body text. |
| `author` | object | Groups public author details when available. |
| `author.name` | string | Gives the public Reddit username of the author. |
| `subreddit` | string | Gives the Reddit community where the comment was posted. |
| `createdAt` | string | Gives the UTC creation time. |
| `editedAt` | string | Gives the UTC edit time when the archive supplies it. |
| `url` | string | Gives the public Reddit URL for the comment. |
| `score` | integer | Gives the engagement score recorded by the archive. |
| `flair` | string | Gives the comment flair when available. |
| `parent` | object | Groups the parent post or comment when the archive supplies the relationship. |
| `parent.id` | string | Gives the stable ID of the parent item. |
| `parent.type` | string | Identifies the parent as `post` or `comment`. |
| `post` | object | Groups related post details when the archive supplies them. |
| `post.id` | string | Gives the stable ID of the related post. |
| `post.title` | string | Gives the title of the related post when available. |
| `post.url` | string | Gives the public URL of the related post when available. |
| `moderation` | object | Groups moderation states recorded for the comment. |
| `moderation.locked` | boolean | Says whether the archive marks the comment as locked. |
| `moderation.archived` | boolean | Says whether the archive marks the comment as archived on Reddit. |
| `moderation.stickied` | boolean | Says whether the archive marks the comment as stickied. |
| `provenance` | object | Groups the archive source and coverage facts for the comment. |
| `provenance.archiveName` | string | Gives the name of the public archive that supplied the comment. |
| `provenance.coverage` | object | Groups known time and volume coverage for the target. |
| `provenance.coverage.earliestCreatedAt` | string | Gives the earliest creation time covered for the target. |
| `provenance.coverage.latestCreatedAt` | string | Gives the latest creation time covered for the target. |
| `provenance.coverage.availableRecordCount` | integer | Gives the number of records the archive reports for the target. |

**Genuine comment row**

```json
{
  "recordType": "comment",
  "id": "ghh2v1p",
  "text": "Happy to write up an answer to this, but in the meantime you could look at this answer [here](https://www.reddit.com/r/AskHistorians/comments/8dgspn/were_the_crimes_of_the_khmer_rouge_a_unique_form/) and  [here](https://www.reddit.com/r/AskHistorians/comments/9506jd/how_controverse_is_the_term_genocide_among/) or for an answer I made for my youtube channel addressing this question [here](https://youtu.be/D7JkO0ZmQHk)",
  "subreddit": "AskHistorians",
  "createdAt": "2020-12-30T05:49:01.000Z",
  "url": "https://www.reddit.com/r/AskHistorians/comments/kmtys6/what_made_the_cambodian_genocide_a_genocide/ghh2v1p/",
  "provenance": {
    "archiveName": "arctic-shift.photon-reddit.com",
    "coverage": {
      "earliestCreatedAt": "2020-12-30T03:28:06.000Z",
      "latestCreatedAt": "2020-12-31T21:41:27.000Z",
      "availableRecordCount": 5
    }
  },
  "author": {
    "name": "ShadowsofUtopia"
  },
  "editedAt": "2021-01-01T00:48:27.000Z",
  "score": 5,
  "flair": "Cambodian History | The Khmer Rouge",
  "parent": {
    "id": "kmtys6",
    "type": "post"
  },
  "post": {
    "id": "kmtys6",
    "title": "What made the Cambodian Genocide a genocide?",
    "url": "https://www.reddit.com/r/AskHistorians/comments/kmtys6/what_made_the_cambodian_genocide_a_genocide/"
  },
  "moderation": {
    "locked": false,
    "stickied": false
  }
}
```

#### 💳 Pricing

**How charging works**

Pricing uses one event for each public archived Reddit post or comment saved to the dataset. A run with no saved post or comment does not create this result event. The current price is shown in the Pricing tab.

Use `maxItems` to bound post and comment rows. For post ID or Reddit URL runs with comments, `maxCommentsPerPost` bounds the comments. Leave either limit empty to return all available results until the source is exhausted.

#### 🔌 Integrations

Run the Actor in Apify Console or through the Apify API. Use the dataset link to download the rows or pass them to another step in your workflow. Apify schedules and webhooks can help with repeat runs and run notifications.

https://www.youtube.com/watch?v=bNACk1\_S\_6w\&list=PLObrtcm1Kw6MUrlLNDbK9QRg8VDJg0gOW\&index=4

#### ❓ FAQ

##### Can I choose posts, comments, or both?

Yes. Set `resultType` to `posts`, `comments`, or `both`. Every row says `post` or `comment` in `recordType`.

##### Can I search inside comment text?

Yes. Set `searchPhrase`. It checks post titles and body text, and it checks comment body text when comments are in scope.

##### How do the date fields work?

`startDate` and `endDate` use UTC and `YYYY-MM-DD`. Records before the start date or after the end date are skipped.

##### Can one run use several target kinds?

Choose one `discoveryMethod` per run: `subreddit`, `author`, `post`, or `url`. You can submit several values of that kind. Use separate runs for different search setups.

##### Can I get every comment in a thread?

A post ID or Reddit URL can be used for a run where comments are selected. The archive may not contain every comment or ancestor, so the Actor does not promise a complete nested tree.

##### Why is a field missing?

Many fields are optional. A value is included when the archive supplies it, and missing source facts are not filled in.

##### Does it read live or private Reddit content?

No. It retrieves public archived content. It is not a live feed, cannot access private communities, and cannot restore content absent from the archive, including deleted or removed records.

##### Do I need a Reddit login or API key?

No. The Actor retrieves public archive content without a Reddit login, OAuth or API key, or a customer-supplied source credential.

##### What happens when I leave a limit empty?

`maxItems` returns all available posts or comments until the source is exhausted. `maxCommentsPerPost` does the same for available comments under a post target. Use a limit when you want less data to review.

##### How can I keep a first run easy to review?

Use one target, a narrow date range, and a small `maxItems` value. For a post target with comments, set `maxCommentsPerPost` too.

### 📝 Changelog

**0.0: Initial release**

### 🆘 Support

For issues, questions, or feature requests, [file a ticket](https://console.apify.com/actors/maximedupre~reddit-historical-archive-scraper/issues) and I'll fix or implement it in less than 24h 🫡

### 🔗 Related Actors

- [Reddit Comments Search Scraper](https://apify.com/maximedupre/reddit-comments-search-scraper). Use it for focused public comment keyword searches.
- [Reddit Scraper](https://apify.com/maximedupre/reddit-scraper). Use it for broader public Reddit post, comment, community, or user collection.
- [Reddit Media Downloader](https://apify.com/maximedupre/reddit-media-downloader). Use it when you need source-hosted media links from Reddit posts.
- [Reddit Historical Archive Scraper](https://apify.com/automation-lab/reddit-historical-archive-scraper). Use it to compare another historical Reddit archive workflow.
- [Reddit Historical Archive Scraper — RAG & Comment Search](https://apify.com/khadinakbar/reddit-archive-scraper). Use it when you need historical Reddit text for RAG or comment search.

**Made with ❤️ by Maxime Dupré**

# Actor input Schema

## `resultType` (type: `string`):

Choose whether to return posts, comments, or both.

## `discoveryMethod` (type: `string`):

Choose the kind of public Reddit target to search.

## `subredditNames` (type: `array`):

Enter one or more subreddit names without the r/ prefix. The Actor searches the selected historical date range in each subreddit.

## `authorNames` (type: `array`):

Enter one or more Reddit usernames without the u/ prefix. The Actor searches posts, comments, or both for each author.

## `postIds` (type: `array`):

Enter one or more Reddit post IDs. The Actor retrieves matching archived posts or their comments in the selected date range.

## `redditUrls` (type: `array`):

Enter one or more public Reddit URLs for archived targets. Use a full URL that starts with https://.

## `startDate` (type: `string`):

Optional lower creation-date bound. Records created before this UTC date are skipped. Use YYYY-MM-DD.

## `endDate` (type: `string`):

Optional upper creation-date bound. Records created after this UTC date are skipped. Use YYYY-MM-DD.

## `searchPhrase` (type: `string`):

Optional phrase to find in post titles and body text, or in comment bodies. With Posts and comments, the phrase is checked in both record types.

## `minScore` (type: `integer`):

Optional minimum engagement score. Records with a lower score are skipped.

## `sortOrder` (type: `string`):

Choose whether older or newer records appear first.

## `maxCommentsPerPost` (type: `integer`):

Optional comment limit for post ID or Reddit URL targets when the selected Result type includes comments. Leave empty to return all available comments until the source is exhausted.

## `maxItems` (type: `integer`):

Optional limit for returned posts or comments in this run. Leave empty to return all available records until the source is exhausted.

## Actor input object example

```json
{
  "resultType": "posts",
  "discoveryMethod": "subreddit",
  "subredditNames": [
    "AskReddit"
  ],
  "sortOrder": "newest",
  "maxItems": 100
}
```

# Actor output Schema

## `dataset` (type: `string`):

Open the archived Reddit posts and comments collected by this run.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "resultType": "posts",
    "discoveryMethod": "subreddit",
    "subredditNames": [
        "AskReddit"
    ],
    "maxItems": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("maximedupre/reddit-historical-archive-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "resultType": "posts",
    "discoveryMethod": "subreddit",
    "subredditNames": ["AskReddit"],
    "maxItems": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("maximedupre/reddit-historical-archive-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "resultType": "posts",
  "discoveryMethod": "subreddit",
  "subredditNames": [
    "AskReddit"
  ],
  "maxItems": 100
}' |
apify call maximedupre/reddit-historical-archive-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,maximedupre/reddit-historical-archive-scraper"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/e9hE0tNUFh7QacUYf/builds/3had6f1HacqUD3rx3/openapi.json
